miniswen 1.3.2 → 1.3.4
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/README.md +1 -0
- data/lib/miniswen/agent.rb +1 -1
- data/lib/miniswen/cli.rb +10 -0
- data/lib/miniswen/jail.rb +49 -0
- data/lib/miniswen/local.rb +9 -1
- data/lib/miniswen/ruby_llm.rb +9 -8
- data/lib/miniswen/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d3124b6837dc7ec83d7abb8ec1a49da77b96767a0ab0e564d99b1cc639d6bf4f
|
|
4
|
+
data.tar.gz: b16c938ec5c39af746923c5814583e22ff576d357332c579ef8f7c0d9bdc73b1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3084347bf1ccf8c6fbcab21af5b1699ee00b30d27dc5f8dfe7610c684bf27709b47e82658ec5a4e00234ce235c27a180f532f1f520ec8613e20f6e02634c96a7
|
|
7
|
+
data.tar.gz: d7a7873f8af98236c23d32809b2361409b0f0d27a310979b93bb705feafab0d26dfac3764c4378ade8de2e56943b54f6746e258e8a675861ec93709dada0542a
|
data/README.md
CHANGED
|
@@ -15,6 +15,7 @@ lemans is a harness for benchmarking coding agents, the Ruby way:
|
|
|
15
15
|
- Ruby 3.4+ is required to run `lemans`
|
|
16
16
|
- Daytona account (API token) or Docker (for local sandboxes)
|
|
17
17
|
- Some LLM provider/proxy credentials (e.g., OpenRouter)
|
|
18
|
+
- iproute2 in the sandbox image, so a jailed agent keeps loopback while cut off the internet
|
|
18
19
|
|
|
19
20
|
## Getting started
|
|
20
21
|
|
data/lib/miniswen/agent.rb
CHANGED
|
@@ -513,7 +513,7 @@ module Miniswen
|
|
|
513
513
|
body = e.response&.body.to_s
|
|
514
514
|
detail = body.empty? ? e.message : "#{e.message}: #{body[0, 1000]}"
|
|
515
515
|
raise InfrastructureError, "miniswen: the model call failed: #{detail}"
|
|
516
|
-
rescue Faraday::SSLError, Faraday::ConnectionFailed, Faraday::TimeoutError => e
|
|
516
|
+
rescue Faraday::SSLError, Faraday::ConnectionFailed, Faraday::TimeoutError, Faraday::ParsingError => e
|
|
517
517
|
raise InfrastructureError, "miniswen: the model call failed: #{e.class}: #{e.message}"
|
|
518
518
|
end
|
|
519
519
|
|
data/lib/miniswen/cli.rb
CHANGED
|
@@ -22,6 +22,7 @@ module Miniswen
|
|
|
22
22
|
@atif_path = nil
|
|
23
23
|
@refresh_registry = false
|
|
24
24
|
@skip_registry_refresh = false
|
|
25
|
+
@jail = false
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def run
|
|
@@ -42,6 +43,9 @@ module Miniswen
|
|
|
42
43
|
if @docker_id
|
|
43
44
|
require "miniswen/environment/docker"
|
|
44
45
|
Environment::Docker.new(@docker_id)
|
|
46
|
+
elsif @jail
|
|
47
|
+
require "miniswen/jail"
|
|
48
|
+
Jail.new.start
|
|
45
49
|
else
|
|
46
50
|
Local.new
|
|
47
51
|
end
|
|
@@ -53,6 +57,8 @@ module Miniswen
|
|
|
53
57
|
rescue StandardError => e
|
|
54
58
|
write_results(agent.partial_result(error_message(e)))
|
|
55
59
|
raise
|
|
60
|
+
ensure
|
|
61
|
+
environment.stop
|
|
56
62
|
end
|
|
57
63
|
|
|
58
64
|
write_results(result)
|
|
@@ -151,6 +157,10 @@ module Miniswen
|
|
|
151
157
|
@docker_id = v
|
|
152
158
|
end
|
|
153
159
|
|
|
160
|
+
opts.on("--jail", "Run every command in its own namespaces: none of the harness's environment, no network, read-only system, none of its files") do
|
|
161
|
+
@jail = true
|
|
162
|
+
end
|
|
163
|
+
|
|
154
164
|
opts.on("--refresh-registry", "Refresh the model registry, persist it, and exit") do
|
|
155
165
|
@refresh_registry = true
|
|
156
166
|
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "miniswen/local"
|
|
4
|
+
|
|
5
|
+
module Miniswen
|
|
6
|
+
# Runs every command in its own namespaces: none of the harness's environment,
|
|
7
|
+
# no network, read-only system, none of its files.
|
|
8
|
+
class Jail < Local
|
|
9
|
+
SETUP = <<~SH
|
|
10
|
+
set -eu
|
|
11
|
+
ip link set lo up
|
|
12
|
+
for dir in /usr /etc /opt; do mount -o bind,ro "$dir" "$dir"; done
|
|
13
|
+
for dir in /tmp /run /root; do mkdir -p "/var/lib/miniswen$dir" && mount --bind "/var/lib/miniswen$dir" "$dir"; done
|
|
14
|
+
echo ready
|
|
15
|
+
exec sleep infinity
|
|
16
|
+
SH
|
|
17
|
+
|
|
18
|
+
def initialize(workdir: Dir.pwd)
|
|
19
|
+
@workdir = workdir
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def start
|
|
23
|
+
_, @stdout, @stderr, @holder = Open3.popen3(
|
|
24
|
+
"unshare", "--net", "--mount", "--pid", "--fork", "--kill-child", "--mount-proc", "sh", "-c", SETUP, pgroup: true
|
|
25
|
+
)
|
|
26
|
+
return self if @stdout.gets == "ready\n"
|
|
27
|
+
|
|
28
|
+
raise InfrastructureError, "jail did not start: #{@stderr.read}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def stop
|
|
32
|
+
Process.kill(:KILL, -@holder.pid) if @holder.alive?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def spawn_arguments(command, env)
|
|
38
|
+
[ ENV.to_h.merge(env.to_h).slice(*container_variables),
|
|
39
|
+
"nsenter", "--target", @holder.pid.to_s, "--net", "--mount", "--pid=/proc/#{@holder.pid}/ns/pid_for_children", "--wd=#{@workdir}",
|
|
40
|
+
"--", "setpriv", "--bounding-set=-all", "--inh-caps=-all", "--no-new-privs", "--", "sh", "-c", command ]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def spawn_options = super.merge(unsetenv_others: true)
|
|
44
|
+
|
|
45
|
+
def container_variables
|
|
46
|
+
@container_variables ||= File.read("/proc/1/environ").split("\0").map { it.split("=").first } | Agent::EXEC_ENV.keys
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/miniswen/local.rb
CHANGED
|
@@ -9,8 +9,10 @@ module Miniswen
|
|
|
9
9
|
class Local < Environment
|
|
10
10
|
TIMEOUT_EXIT_CODE = 124
|
|
11
11
|
|
|
12
|
+
# Always through a shell: Ruby execs a metacharacter-free string directly,
|
|
13
|
+
# and a missing binary would then raise ENOENT here instead of exiting 127.
|
|
12
14
|
def exec(command, timeout: nil, env: nil)
|
|
13
|
-
Open3.popen2e(
|
|
15
|
+
Open3.popen2e(*spawn_arguments(command, env), **spawn_options) do |stdin, io, wait_thr|
|
|
14
16
|
stdin.close
|
|
15
17
|
reader = Thread.new { io.read }
|
|
16
18
|
|
|
@@ -25,8 +27,14 @@ module Miniswen
|
|
|
25
27
|
end
|
|
26
28
|
end
|
|
27
29
|
|
|
30
|
+
def stop = nil
|
|
31
|
+
|
|
28
32
|
private
|
|
29
33
|
|
|
34
|
+
def spawn_arguments(command, env) = [ env || {}, "sh", "-c", command ]
|
|
35
|
+
|
|
36
|
+
def spawn_options = { pgroup: true }
|
|
37
|
+
|
|
30
38
|
def kill_group(pid)
|
|
31
39
|
Process.kill(:KILL, -pid)
|
|
32
40
|
rescue Errno::ESRCH, Errno::EPERM
|
data/lib/miniswen/ruby_llm.rb
CHANGED
|
@@ -8,9 +8,10 @@ RubyLLM.configure do |config|
|
|
|
8
8
|
config.logger = Logger.new(IO::NULL) unless ENV["MINISWEN_DEBUG"] == "1"
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
# About
|
|
11
|
+
# About ten minutes of retries (1, 2, 4, ... 256s plus jitter): provider
|
|
12
|
+
# outages and rate-limit windows outlast the minute this used to allow.
|
|
12
13
|
RubyLLM.configure do |config|
|
|
13
|
-
config.max_retries =
|
|
14
|
+
config.max_retries = 9
|
|
14
15
|
config.retry_interval = 1
|
|
15
16
|
end
|
|
16
17
|
|
|
@@ -36,13 +37,13 @@ module Miniswen
|
|
|
36
37
|
end
|
|
37
38
|
end
|
|
38
39
|
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
module
|
|
43
|
-
def retry_exceptions = super + [ Faraday::SSLError ]
|
|
40
|
+
# ruby_llm lists SSL errors as fatal, but a handshake reset never sent the
|
|
41
|
+
# request; a 200 with a truncated JSON body is the same dropped connection
|
|
42
|
+
# one step later.
|
|
43
|
+
module RetryTransientFailures
|
|
44
|
+
def retry_exceptions = super + [ Faraday::SSLError, Faraday::ParsingError ]
|
|
44
45
|
end
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
RubyLLM::Providers::OpenRouter.prepend(Miniswen::VerbatimReasoningDetails)
|
|
48
|
-
RubyLLM::Connection.prepend(Miniswen::
|
|
49
|
+
RubyLLM::Connection.prepend(Miniswen::RetryTransientFailures)
|
data/lib/miniswen/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: miniswen
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Svyatoslav Kryukov
|
|
@@ -58,6 +58,7 @@ files:
|
|
|
58
58
|
- lib/miniswen/cli/reporter.rb
|
|
59
59
|
- lib/miniswen/environment.rb
|
|
60
60
|
- lib/miniswen/environment/docker.rb
|
|
61
|
+
- lib/miniswen/jail.rb
|
|
61
62
|
- lib/miniswen/local.rb
|
|
62
63
|
- lib/miniswen/ruby_llm.rb
|
|
63
64
|
- lib/miniswen/testing.rb
|