pwn 0.5.655 → 0.5.657

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: 6e41d71c8fc29542a4190d697603d8626138e2d2a10fba03d39637efb9103a1f
4
- data.tar.gz: 3dc38eeb6c27e6e8899b5fb619f0b4d3afe9e31b3bf37ea7460197b48eac290e
3
+ metadata.gz: af75906b3c582dea27fb9ec31f19c7d3774aabcc928549cee27df74ad75220f3
4
+ data.tar.gz: c3424966d7b298c78b3edac59a777ee8e927f130ecefbf84fd0a46371256900e
5
5
  SHA512:
6
- metadata.gz: 8f7ca9fa6a7f7784da54fd956eefd976d136c1226f48d5d7ecfe2911220b5b706b54d15b478c7cd69e0a751190ff876dc2f642068a9cc13320ffa3254929687d
7
- data.tar.gz: c00d46ffc0b34013288c32ba279805ea9a617bc79eaa81e8b9e4ada48ecb5e5d6ab8d7b0926b4d2f0277e1cce3bd704407e85759f7a5196826ca96eb71db2e31
6
+ metadata.gz: 396a1f16a96e071396689cdfde74364ee9bae88544275f037137007b5ddd94dc1fc37f181a99ef239db5339b5fb24ecf750605ba67dd28bd62969ec9360ed5f6
7
+ data.tar.gz: 976c310a1e93bac33b383bcb7702a6350f2268062e71a0bb73ee28454d6ea23798fc654744b88dad048472d721d2417713d5664e81f39e819ddd2ccefe51c555
@@ -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,10 +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 deepen² remote was still burning full 12-step plans even when
120
- # overconfidence sat just under the 0.25 gate (0.242 on grok). Always
121
- # cap to 8 for ALL engines while budget fingerprints dominate; finish-
122
- # under-N is the skill gap, more headroom only yields empty terminals.
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.
123
155
  n = [n, 8].min
124
156
  end
125
157
  n
@@ -141,11 +173,22 @@ module PWN
141
173
  # P17 — gate lowered 0.25→0.20: grok lived at 0.242 and never tripped,
142
174
  # leaving force_plan off while still thrashing tool budgets.
143
175
  bad = brier > 0.35 || over > 0.20
176
+ # P28 — autonomy: overconfidence must force plan+critic and shrink thrash,
177
+ # but must NOT collapse multi-step remote work to 8 iters (user-visible
178
+ # "stop to confirm next step" / early text-only handoffs). Local models
179
+ # keep the harsh 8; remote engines keep a usable multi-step runway.
180
+ remote_cap = 40
181
+ local_cap = 8
182
+ cap = if bad
183
+ (eng == :ollama ? local_cap : remote_cap)
184
+ else
185
+ 25
186
+ end
144
187
  {
145
188
  overconfident: bad,
146
189
  force_plan: bad,
147
190
  force_critic: bad,
148
- max_iters_cap: bad ? 8 : 25,
191
+ max_iters_cap: cap,
149
192
  cal: cal
150
193
  }
151
194
  rescue StandardError
@@ -281,7 +324,7 @@ module PWN
281
324
  plan_prompt = if hot
282
325
  '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
326
  else
284
- 'Before acting: (1) list the exact tool calls (name + key args) you will make, in order; (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.'
327
+ '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
328
  end
286
329
  plan_msg = call_engine(
287
330
  messages: messages + [{ role: 'user', content: plan_prompt }],
@@ -592,15 +635,19 @@ module PWN
592
635
  # nothing the user (or ORM) can use.
593
636
  # P17 deepen — when budget_hot, force text-only on the LAST TWO
594
637
  # iters so a final tool_calls batch cannot burn the terminal slot.
595
- text_only_iters = budget_exhaustion_hot? ? 2 : 1
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
596
641
  last_iter = (i >= max_iters - text_only_iters)
597
642
  if last_iter
598
643
  tag = i >= max_iters - 1 ? 'FINAL ITERATION' : 'PENULTIMATE — wrap up'
599
644
  messages << {
600
645
  role: 'user',
601
646
  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 blocked, say what failed and the next single step.'
647
+ 'Write the best complete answer you can from evidence already in this ' \
648
+ 'transcript. If the goal is unfinished, report exactly what is done, ' \
649
+ 'what is blocked, and the concrete remaining work — do NOT ask the ' \
650
+ 'user to confirm the next step.'
604
651
  }
605
652
  end
606
653
 
@@ -630,6 +677,19 @@ module PWN
630
677
  messages << msg
631
678
 
632
679
  if calls.empty?
680
+ # P28 — refuse polite mid-goal handoffs so multi-step tasks stay autonomous.
681
+ if incomplete_final?(text: text, last_iter: last_iter) && turn_fails['incomplete_final'].to_i < 2
682
+ turn_fails['incomplete_final'] += 1
683
+ warn "[pwn-ai/loop] incomplete final on iter=#{i}; continuing autonomously"
684
+ messages << {
685
+ role: 'user',
686
+ content: '[pwn-ai/p28] That reply handed control back before the goal was done. ' \
687
+ 'Do NOT ask the user to confirm the next step. Continue with the ' \
688
+ 'necessary tool calls now and finish the goal autonomously. Only ' \
689
+ 'emit a final answer when the request is complete or truly blocked.'
690
+ }
691
+ next
692
+ end
633
693
  append_session(session_id: session_id, role: 'assistant', content: text)
634
694
  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
695
  return text
@@ -771,6 +831,10 @@ module PWN
771
831
  :hindsight - C3 HER-relabel failures (Boolean, default true)
772
832
  :verify_as_reward - E3 ground every final via extro_verify (Boolean)
773
833
 
834
+ P28 autonomy: incomplete-final detector refuses mid-goal handoffs;
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).
837
+
774
838
  #{self}.authors
775
839
  USAGE
776
840
  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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PWN
4
- VERSION = '0.5.655'
4
+ VERSION = '0.5.657'
5
5
  end
@@ -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
@@ -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`: "}]}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.655
4
+ version: 0.5.657
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.