pwn 0.5.656 → 0.5.658
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/lib/pwn/ai/agent/loop.rb +11 -7
- data/lib/pwn/ai/agent/tools/shell.rb +83 -8
- data/lib/pwn/banner/white_rabbit.rb +1 -1
- data/lib/pwn/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: 84155e448029c5d41ebd8dda5e4c00147a3283ebf8258c42f2fdf7cb3f159616
|
|
4
|
+
data.tar.gz: ceecb743a07ddf4d0a2f45434f1b7816e859754762d60f8932f9dd1f4a83d80a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5cb1bf7948fa027253fa5e2aeb2204d917bf51ab4ebf2710073b8ca3c4fa07f53012a3e8f98e5db5f3dabda050b405119dadf78b7a5583476da35fd11af5c5c7
|
|
7
|
+
data.tar.gz: 07e6832ea6946949d3042f62b03930d5e6f882dd8f246449f314c994459b4f16ce4e32700ab4467b55d44e01f5ba35dbc3788f742d44a7fd79b36026b2d61ccf
|
data/lib/pwn/ai/agent/loop.rb
CHANGED
|
@@ -146,12 +146,13 @@ module PWN
|
|
|
146
146
|
# is "iteration budget exhausted" ×N; more headroom only produces more
|
|
147
147
|
# empty terminal failures for ORM/PRM/DPO.
|
|
148
148
|
if budget_exhaustion_hot?
|
|
149
|
-
# P17
|
|
150
|
-
#
|
|
151
|
-
#
|
|
152
|
-
#
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
# P17 — always 8 for ALL engines while budget fingerprints dominate.
|
|
150
|
+
# P28 remote runway (40) applies only to W3 overconf above; stacking
|
|
151
|
+
# a 40 hot-cap on top re-opens multi-dozen-iter thrash and is the
|
|
152
|
+
# 2026-08-04 regression (22 exhaust hits / day). Finish-under-8 is
|
|
153
|
+
# the sustained drop; incomplete_final? keeps multi-step autonomy
|
|
154
|
+
# inside that budget without polite handoffs.
|
|
155
|
+
n = [n, 8].min
|
|
155
156
|
end
|
|
156
157
|
n
|
|
157
158
|
rescue StandardError
|
|
@@ -634,7 +635,9 @@ module PWN
|
|
|
634
635
|
# nothing the user (or ORM) can use.
|
|
635
636
|
# P17 deepen — when budget_hot, force text-only on the LAST TWO
|
|
636
637
|
# iters so a final tool_calls batch cannot burn the terminal slot.
|
|
637
|
-
|
|
638
|
+
# P17 deepen³ — under hot, force text-only on last THREE of the
|
|
639
|
+
# 8-iter cap so a late tool binge cannot burn every salvage slot.
|
|
640
|
+
text_only_iters = budget_exhaustion_hot? ? 3 : 1
|
|
638
641
|
last_iter = (i >= max_iters - text_only_iters)
|
|
639
642
|
if last_iter
|
|
640
643
|
tag = i >= max_iters - 1 ? 'FINAL ITERATION' : 'PENULTIMATE — wrap up'
|
|
@@ -830,6 +833,7 @@ module PWN
|
|
|
830
833
|
|
|
831
834
|
P28 autonomy: incomplete-final detector refuses mid-goal handoffs;
|
|
832
835
|
W3 overconf max_iters_cap is 40 on remote engines (8 on ollama).
|
|
836
|
+
P17 budget-hot always caps max_iters to 8 for ALL engines (not only ollama).
|
|
833
837
|
|
|
834
838
|
#{self}.authors
|
|
835
839
|
USAGE
|
|
@@ -6,6 +6,13 @@ require 'pwn/ai/agent/registry'
|
|
|
6
6
|
|
|
7
7
|
# Run a shell command on the pwn host. Lifted from the bash branch of the
|
|
8
8
|
# legacy :pwn_ai_hook (repl.rb).
|
|
9
|
+
#
|
|
10
|
+
# Timeout path must NOT leave Open3's pipe-reader threads racing a closed
|
|
11
|
+
# pipe — that surfaces as noisy:
|
|
12
|
+
# #<Thread:0x... open3.rb:664 run> terminated with exception
|
|
13
|
+
# (report_on_exception is true): IOError stream closed in another thread
|
|
14
|
+
# Fix: own the popen3 lifecycle, process-group kill on timeout, and silence
|
|
15
|
+
# report_on_exception on the reader threads before closing pipes.
|
|
9
16
|
PWN::AI::Agent::Registry.register(
|
|
10
17
|
name: 'shell',
|
|
11
18
|
toolset: 'terminal',
|
|
@@ -29,16 +36,84 @@ PWN::AI::Agent::Registry.register(
|
|
|
29
36
|
timeout = (args[:timeout] || 120).to_i
|
|
30
37
|
raise ArgumentError, 'command is required' if cmd.strip.empty?
|
|
31
38
|
|
|
32
|
-
stdout =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
stdout = +''
|
|
40
|
+
stderr = +''
|
|
41
|
+
exitstatus = nil
|
|
42
|
+
timed_out = false
|
|
43
|
+
|
|
44
|
+
# pgroup: true => child is session/process-group leader; kill(-pid)
|
|
45
|
+
# reaps the whole tree (shell + grandchildren) on timeout.
|
|
46
|
+
Open3.popen3(cmd, pgroup: true) do |stdin, out_io, err_io, wait_thr|
|
|
47
|
+
stdin.close
|
|
48
|
+
pid = wait_thr.pid
|
|
49
|
+
|
|
50
|
+
out_reader = Thread.new { out_io.read.to_s }
|
|
51
|
+
err_reader = Thread.new { err_io.read.to_s }
|
|
52
|
+
# Prevent Ruby from dumping IOError to $stderr when we close pipes
|
|
53
|
+
# out from under these readers on the timeout path.
|
|
54
|
+
out_reader.report_on_exception = false
|
|
55
|
+
err_reader.report_on_exception = false
|
|
56
|
+
|
|
57
|
+
begin
|
|
58
|
+
Timeout.timeout(timeout) do
|
|
59
|
+
# Wait for the child; readers drain pipes concurrently.
|
|
60
|
+
wait_thr.value
|
|
61
|
+
end
|
|
62
|
+
rescue Timeout::Error
|
|
63
|
+
timed_out = true
|
|
64
|
+
begin
|
|
65
|
+
Process.kill('TERM', -pid)
|
|
66
|
+
rescue Errno::ESRCH, Errno::EPERM
|
|
67
|
+
nil
|
|
68
|
+
end
|
|
69
|
+
# Brief grace, then hard kill the process group.
|
|
70
|
+
sleep 0.2
|
|
71
|
+
begin
|
|
72
|
+
Process.kill('KILL', -pid)
|
|
73
|
+
rescue Errno::ESRCH, Errno::EPERM
|
|
74
|
+
nil
|
|
75
|
+
end
|
|
76
|
+
begin
|
|
77
|
+
wait_thr.value
|
|
78
|
+
rescue StandardError
|
|
79
|
+
nil
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Ensure pipes are closed so readers unblock even if the child
|
|
84
|
+
# ignored signals or left descendants holding fds.
|
|
85
|
+
begin
|
|
86
|
+
out_io.close unless out_io.closed?
|
|
87
|
+
rescue StandardError
|
|
88
|
+
nil
|
|
89
|
+
end
|
|
90
|
+
begin
|
|
91
|
+
err_io.close unless err_io.closed?
|
|
92
|
+
rescue StandardError
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Join readers; swallow the IOError that arrives when the pipe was
|
|
97
|
+
# closed mid-read (timeout path). report_on_exception is already off.
|
|
98
|
+
begin
|
|
99
|
+
stdout = out_reader.value.to_s
|
|
100
|
+
rescue StandardError
|
|
101
|
+
stdout = +''
|
|
102
|
+
end
|
|
103
|
+
begin
|
|
104
|
+
stderr = err_reader.value.to_s
|
|
105
|
+
rescue StandardError
|
|
106
|
+
stderr = +''
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
unless timed_out
|
|
110
|
+
status = wait_thr.value
|
|
111
|
+
exitstatus = status&.exitstatus
|
|
37
112
|
end
|
|
38
|
-
rescue Timeout::Error
|
|
39
|
-
return { stdout: stdout, stderr: stderr, exit: nil, error: "timeout after #{timeout}s" }
|
|
40
113
|
end
|
|
41
114
|
|
|
42
|
-
{ stdout: stdout, stderr: stderr, exit:
|
|
115
|
+
return { stdout: stdout, stderr: stderr, exit: nil, error: "timeout after #{timeout}s" } if timed_out
|
|
116
|
+
|
|
117
|
+
{ stdout: stdout, stderr: stderr, exit: exitstatus }
|
|
43
118
|
}
|
|
44
119
|
)
|
data/lib/pwn/version.rb
CHANGED