pwn 0.5.655 → 0.5.656
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ba4560cb04bae940cc23704a47b54f60a33a47587531101f340beb12897b12e9
|
|
4
|
+
data.tar.gz: 7635dbe7cb031343f1df74c430b22c1a9db3b2a23d3990812573e0785dd4aaa1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e21ee1d6b4e66d692b6ad5896f1ec0f87426258854911d9dc7759d567dd3b472ab8dc99409ca33e7412afc7bab00d0174d56fa9cc979074b0192cc2bc43e0459
|
|
7
|
+
data.tar.gz: fdbed596d420d497b1874c1f5473c513327813b2625b2b9bedf7f21d5dbe51da26598c6e5a2082ebc7d2735f4f56e69a3f8feb9f99d2883899230c39b02e6b09
|
data/lib/pwn/ai/agent/loop.rb
CHANGED
|
@@ -100,6 +100,36 @@ module PWN
|
|
|
100
100
|
false
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
+
# P28 — incomplete / handoff finals: model emitted text-only before the
|
|
104
|
+
# goal was done ("shall I proceed?", "next step:", "want me to…").
|
|
105
|
+
# Loop.run treats no-tool_calls as FINAL; this detector lets us refuse
|
|
106
|
+
# that handoff and keep the tool loop alive for multi-step autonomy.
|
|
107
|
+
INCOMPLETE_FINAL_RX = /
|
|
108
|
+
\b(shall\s+i|should\s+i|may\s+i|can\s+i|want\s+me\s+to|do\s+you\s+want\s+me|
|
|
109
|
+
next\s+single\s+step|next\s+step\s*:|awaiting\s+your\s+(ok|approval|go-ahead|confirmation)|
|
|
110
|
+
if\s+you(?:'d|\s+would)\s+like\s+me\s+to|say\s+the\s+word|confirm\s+(before|and\s+i)|
|
|
111
|
+
ready\s+to\s+proceed|ok\s+to\s+(proceed|continue|apply)|proceed\?|
|
|
112
|
+
continue\?|before\s+i\s+(apply|change|run|continue|proceed)|
|
|
113
|
+
once\s+you\s+(confirm|approve)|let\s+me\s+know\s+if|
|
|
114
|
+
i(?:'ll|\s+will)\s+wait\b|waiting\s+for\s+(your\s+)?(go|ok|approval|confirmation)
|
|
115
|
+
)\b
|
|
116
|
+
/ix
|
|
117
|
+
|
|
118
|
+
private_class_method def self.incomplete_final?(opts = {})
|
|
119
|
+
text = opts[:text].to_s
|
|
120
|
+
return false if text.strip.empty?
|
|
121
|
+
# Hard last-iter forces a real final; do not bounce that forever.
|
|
122
|
+
return false if opts[:last_iter]
|
|
123
|
+
return true if text.match?(INCOMPLETE_FINAL_RX)
|
|
124
|
+
# Short status-only dumps with a trailing question are handoffs.
|
|
125
|
+
return true if text.include?('?') && text.length < 900 &&
|
|
126
|
+
text.match?(/\b(proceed|continue|confirm|apply|next)\b/i)
|
|
127
|
+
|
|
128
|
+
false
|
|
129
|
+
rescue StandardError
|
|
130
|
+
false
|
|
131
|
+
end
|
|
132
|
+
|
|
103
133
|
private_class_method def self.max_iters
|
|
104
134
|
v = (PWN::Env.dig(:ai, :agent, :max_iters) if defined?(PWN::Env))
|
|
105
135
|
n = v.to_i.positive? ? v.to_i : DEFAULT_MAX_ITERS
|
|
@@ -116,11 +146,12 @@ module PWN
|
|
|
116
146
|
# is "iteration budget exhausted" ×N; more headroom only produces more
|
|
117
147
|
# empty terminal failures for ORM/PRM/DPO.
|
|
118
148
|
if budget_exhaustion_hot?
|
|
119
|
-
# P17
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
|
|
149
|
+
# P17 + P28 — finish-under-N while budget fingerprints dominate, but
|
|
150
|
+
# do NOT collapse multi-step remote work to 8 (that re-creates the
|
|
151
|
+
# polite handoff / early empty-final failure mode P28 fixed).
|
|
152
|
+
# Local/ollama stays harsh at 8; remote engines keep the W3 runway.
|
|
153
|
+
hot_cap = active_engine == :ollama ? 8 : 40
|
|
154
|
+
n = [n, hot_cap].min
|
|
124
155
|
end
|
|
125
156
|
n
|
|
126
157
|
rescue StandardError
|
|
@@ -141,11 +172,22 @@ module PWN
|
|
|
141
172
|
# P17 — gate lowered 0.25→0.20: grok lived at 0.242 and never tripped,
|
|
142
173
|
# leaving force_plan off while still thrashing tool budgets.
|
|
143
174
|
bad = brier > 0.35 || over > 0.20
|
|
175
|
+
# P28 — autonomy: overconfidence must force plan+critic and shrink thrash,
|
|
176
|
+
# but must NOT collapse multi-step remote work to 8 iters (user-visible
|
|
177
|
+
# "stop to confirm next step" / early text-only handoffs). Local models
|
|
178
|
+
# keep the harsh 8; remote engines keep a usable multi-step runway.
|
|
179
|
+
remote_cap = 40
|
|
180
|
+
local_cap = 8
|
|
181
|
+
cap = if bad
|
|
182
|
+
(eng == :ollama ? local_cap : remote_cap)
|
|
183
|
+
else
|
|
184
|
+
25
|
|
185
|
+
end
|
|
144
186
|
{
|
|
145
187
|
overconfident: bad,
|
|
146
188
|
force_plan: bad,
|
|
147
189
|
force_critic: bad,
|
|
148
|
-
max_iters_cap:
|
|
190
|
+
max_iters_cap: cap,
|
|
149
191
|
cal: cal
|
|
150
192
|
}
|
|
151
193
|
rescue StandardError
|
|
@@ -281,7 +323,7 @@ module PWN
|
|
|
281
323
|
plan_prompt = if hot
|
|
282
324
|
'Before acting: write AT MOST 3 numbered tool calls (name + key args) that finish the ask. Prefer fewer. LAST line: "p(success)=<0.0-1.0>". Reply ONLY with the plan + that line — no tools, no prose.'
|
|
283
325
|
else
|
|
284
|
-
'Before acting: (1) list the exact tool calls (name + key args)
|
|
326
|
+
'Before acting: (1) list the exact tool calls (name + key args) that FULLY finish the user goal, in order — do not stop at a checkpoint for confirmation; (2) on the LAST line write "p(success)=<0.0-1.0>". Reply ONLY with the numbered plan + that line — do NOT call any tool yet.'
|
|
285
327
|
end
|
|
286
328
|
plan_msg = call_engine(
|
|
287
329
|
messages: messages + [{ role: 'user', content: plan_prompt }],
|
|
@@ -599,8 +641,10 @@ module PWN
|
|
|
599
641
|
messages << {
|
|
600
642
|
role: 'user',
|
|
601
643
|
content: "[pwn-ai/p17] #{tag} — do NOT call any more tools. " \
|
|
602
|
-
'Write the best answer you can from evidence already in this ' \
|
|
603
|
-
'transcript. If
|
|
644
|
+
'Write the best complete answer you can from evidence already in this ' \
|
|
645
|
+
'transcript. If the goal is unfinished, report exactly what is done, ' \
|
|
646
|
+
'what is blocked, and the concrete remaining work — do NOT ask the ' \
|
|
647
|
+
'user to confirm the next step.'
|
|
604
648
|
}
|
|
605
649
|
end
|
|
606
650
|
|
|
@@ -630,6 +674,19 @@ module PWN
|
|
|
630
674
|
messages << msg
|
|
631
675
|
|
|
632
676
|
if calls.empty?
|
|
677
|
+
# P28 — refuse polite mid-goal handoffs so multi-step tasks stay autonomous.
|
|
678
|
+
if incomplete_final?(text: text, last_iter: last_iter) && turn_fails['incomplete_final'].to_i < 2
|
|
679
|
+
turn_fails['incomplete_final'] += 1
|
|
680
|
+
warn "[pwn-ai/loop] incomplete final on iter=#{i}; continuing autonomously"
|
|
681
|
+
messages << {
|
|
682
|
+
role: 'user',
|
|
683
|
+
content: '[pwn-ai/p28] That reply handed control back before the goal was done. ' \
|
|
684
|
+
'Do NOT ask the user to confirm the next step. Continue with the ' \
|
|
685
|
+
'necessary tool calls now and finish the goal autonomously. Only ' \
|
|
686
|
+
'emit a final answer when the request is complete or truly blocked.'
|
|
687
|
+
}
|
|
688
|
+
next
|
|
689
|
+
end
|
|
633
690
|
append_session(session_id: session_id, role: 'assistant', content: text)
|
|
634
691
|
Learning.auto_introspect(session_id: session_id, request: request, final: text, predicted: predicted) if defined?(Learning) && should_auto_introspect?(local: local, turn_fails: turn_fails, iter: i)
|
|
635
692
|
return text
|
|
@@ -771,6 +828,9 @@ module PWN
|
|
|
771
828
|
:hindsight - C3 HER-relabel failures (Boolean, default true)
|
|
772
829
|
:verify_as_reward - E3 ground every final via extro_verify (Boolean)
|
|
773
830
|
|
|
831
|
+
P28 autonomy: incomplete-final detector refuses mid-goal handoffs;
|
|
832
|
+
W3 overconf max_iters_cap is 40 on remote engines (8 on ollama).
|
|
833
|
+
|
|
774
834
|
#{self}.authors
|
|
775
835
|
USAGE
|
|
776
836
|
end
|
|
@@ -54,6 +54,15 @@ module PWN
|
|
|
54
54
|
no tool_calls is treated as your FINAL answer to the user.
|
|
55
55
|
Prefer `pwn_eval` for anything in the PWN:: namespace and `shell`
|
|
56
56
|
for OS commands. Save durable facts with `memory_remember`.
|
|
57
|
+
|
|
58
|
+
AUTONOMY
|
|
59
|
+
Multi-step goals must be finished in one Loop.run. Keep calling
|
|
60
|
+
tools until the request is done or truly blocked. Do NOT stop to
|
|
61
|
+
ask the user to confirm the next step, approve a partial plan, or
|
|
62
|
+
green-light the obvious continuation. Only ask when a credential,
|
|
63
|
+
irreversible destructive action, or missing external decision is
|
|
64
|
+
strictly required. Partial progress reports without completing the
|
|
65
|
+
goal are incorrect behavior.
|
|
57
66
|
"
|
|
58
67
|
end
|
|
59
68
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'base64'
|
|
4
4
|
require 'json'
|
|
5
|
+
require 'rest-client'
|
|
5
6
|
|
|
6
7
|
module PWN
|
|
7
8
|
module Plugins
|
|
@@ -95,13 +96,13 @@ module PWN
|
|
|
95
96
|
return response if raw
|
|
96
97
|
|
|
97
98
|
JSON.parse(response.body, symbolize_names: true)
|
|
98
|
-
rescue RestClient::TooManyRequests
|
|
99
|
+
rescue ::RestClient::TooManyRequests
|
|
99
100
|
@@logger.warn('HackerOne rate limit (429). Sleeping 10s then retrying...')
|
|
100
101
|
sleep 10
|
|
101
102
|
retry
|
|
102
|
-
rescue RestClient::Unauthorized, RestClient::Forbidden,
|
|
103
|
-
RestClient::BadRequest, RestClient::NotFound,
|
|
104
|
-
RestClient::UnprocessableEntity => e
|
|
103
|
+
rescue ::RestClient::Unauthorized, ::RestClient::Forbidden,
|
|
104
|
+
::RestClient::BadRequest, ::RestClient::NotFound,
|
|
105
|
+
::RestClient::UnprocessableEntity => e
|
|
105
106
|
@@logger.error("HackerOne #{e.class}: #{e.response&.body}")
|
|
106
107
|
raise e
|
|
107
108
|
rescue StandardError => e
|
data/lib/pwn/version.rb
CHANGED
|
@@ -1502,5 +1502,35 @@ RSpec.describe 'PWN::AI::Agent reinforced feedback loop', :aggregate_failures do
|
|
|
1502
1502
|
expect(doc).to match(/Last-iter force-final/)
|
|
1503
1503
|
end
|
|
1504
1504
|
end
|
|
1505
|
+
|
|
1506
|
+
describe 'P28 · autonomy (remote overconf runway + incomplete-final)' do
|
|
1507
|
+
it 'sets W3 overconf max_iters_cap to 40 on remote, 8 on ollama' do
|
|
1508
|
+
src = File.read(loop_mod.method(:run).source_location.first)
|
|
1509
|
+
expect(src).to match(/P28/)
|
|
1510
|
+
expect(src).to match(/remote_cap = 40/)
|
|
1511
|
+
expect(src).to match(/local_cap\s*=\s*8/)
|
|
1512
|
+
expect(src).to match(/eng == :ollama \? local_cap : remote_cap/)
|
|
1513
|
+
# budget-hot still always 8 (P17)
|
|
1514
|
+
expect(src).to match(/n = \[n, 8\]\.min/)
|
|
1515
|
+
end
|
|
1516
|
+
|
|
1517
|
+
it 'defines incomplete_final? and continues on mid-goal handoff' do
|
|
1518
|
+
src = File.read(loop_mod.method(:run).source_location.first)
|
|
1519
|
+
expect(src).to match(/incomplete_final\?/)
|
|
1520
|
+
expect(src).to match(/INCOMPLETE_FINAL_RX/)
|
|
1521
|
+
expect(src).to match(%r{\[pwn-ai/p28\]})
|
|
1522
|
+
expect(src).to match(/continuing autonomously/)
|
|
1523
|
+
# last-iter wording no longer coaches "next single step" handoffs
|
|
1524
|
+
expect(src).not_to match(/next single step/)
|
|
1525
|
+
expect(src).to match(/do NOT ask the/)
|
|
1526
|
+
end
|
|
1527
|
+
|
|
1528
|
+
it 'PromptBuilder injects AUTONOMY block' do
|
|
1529
|
+
src = File.read(File.expand_path('../../lib/pwn/ai/agent/prompt_builder.rb', __dir__))
|
|
1530
|
+
expect(src).to match(/AUTONOMY/)
|
|
1531
|
+
expect(src).to match(/Do NOT stop to/)
|
|
1532
|
+
expect(src).to match(/Multi-step goals must be finished in one Loop\.run/)
|
|
1533
|
+
end
|
|
1534
|
+
end
|
|
1505
1535
|
end
|
|
1506
1536
|
# rubocop:enable Metrics/BlockLength
|
data/third_party/pwn_rdoc.jsonl
CHANGED
|
@@ -301,6 +301,7 @@
|
|
|
301
301
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.expose_current_session Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.expose_current_session`: "}]}
|
|
302
302
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.guard_repeated_failure Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.guard_repeated_failure`: "}]}
|
|
303
303
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.help Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.help`: "}]}
|
|
304
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.incomplete_final? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.incomplete_final?`: "}]}
|
|
304
305
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.max_iters Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.max_iters`: "}]}
|
|
305
306
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.normalize_llm Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.normalize_llm`: Supported Method Parameters\n\nmsg = PWN::AI::Agent::Loop.normalize_llm(\n\nresponse: 'required - chat_with_tools response Hash from any provider'\n\n)\n"}]}
|
|
306
307
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.plan_first Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.plan_first`: "}]}
|