pwn 0.5.701 → 0.5.702

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: e51829d4db1ea2aaea43c6c7707f64e6b071fd0535b72e9ac330eaf08798c6e4
4
- data.tar.gz: 5f3f54e56dc52ba72f95edcb33a342a9e865569d21a1b09b2d9e9810b2058c33
3
+ metadata.gz: 5e0f1934072cdfc2dd334d22bcd15774e5756a0bd02bb68962759ceb2e436b98
4
+ data.tar.gz: b092c265b35a9000293ce787252452c8045c4604677a3bd5416a2e4e732c2cce
5
5
  SHA512:
6
- metadata.gz: 650f0bd9930cde7a95973ef32313bdaedb6420b1c0d9ac87e67554e95b419969b1836dc635362d105c7a7b5e8116c2fda191aa902c4716c7451a458a43135469
7
- data.tar.gz: f3ac960a2f8e780d3f9ca316d72ff69ada1353c17cde9c1d51a6d0d12d2bf8fd21af20ac8de8e4d41fa60b9274f136e08426a6c5e9f00cf40064443bb91a55e2
6
+ metadata.gz: 61bdd9c6f370aa44f1cb3e5b00a928cd57acb5f1a40154769079ecec39ac4f0d8ce81c8daac5c5a1f5040b96cff7b7503de230645920531a854f10762bf75058
7
+ data.tar.gz: bc978a06cf9dd7e109696b6e95bfba64dcbc73d3092a9fbdcfe19cdf488dcec0b3c463866a90703050705fdf01bdd0f6a34e93c5f02812631682669499632461
@@ -172,6 +172,7 @@ module PWN
172
172
  private_class_method def self.finish_debug_request!(opts = {})
173
173
  return unless debug_on?(opts)
174
174
  return unless defined?(PWN::Plugins::Log)
175
+ return if opts[:nested]
175
176
 
176
177
  PWN::Plugins::Log.finish_request_log!(
177
178
  iter: opts[:iter],
@@ -572,6 +573,7 @@ module PWN
572
573
 
573
574
  live = effects.reject { |fx| %i[recall store].include?(fx) }
574
575
  return true if live.empty?
576
+ return true if duration_unsatisfied?(request: request)
575
577
  return true if need == :write && !write_verified?(effects: effects)
576
578
  return true if need == :browse && !effects.include?(:browse)
577
579
  return true if need == :any && !effects.intersect?(%i[write browse eval])
@@ -592,6 +594,32 @@ module PWN
592
594
  false
593
595
  end
594
596
 
597
+ HOUR_WORDS = {
598
+ 'one' => 1, 'two' => 2, 'three' => 3, 'four' => 4, 'five' => 5,
599
+ 'six' => 6, 'seven' => 7, 'eight' => 8, 'nine' => 9, 'ten' => 10,
600
+ 'eleven' => 11, 'twelve' => 12, 'thirteen' => 13, 'fourteen' => 14,
601
+ 'fifteen' => 15, 'sixteen' => 16, 'seventeen' => 17, 'eighteen' => 18,
602
+ 'nineteen' => 19, 'twenty' => 20, 'twenty-four' => 24
603
+ }.freeze
604
+
605
+ private_class_method def self.duration_unsatisfied?(opts = {})
606
+ req = opts[:request].to_s
607
+ hours = nil
608
+ if (m = req.match(/\b(\d+)\s*(?:hours?|hrs?)\b/i))
609
+ hours = m[1].to_i
610
+ elsif (m = req.match(/\b(twenty-four|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty)\s*(?:hours?|hrs?)\b/i))
611
+ hours = HOUR_WORDS[m[1].downcase]
612
+ end
613
+ return false if hours.to_i <= 0
614
+
615
+ t0 = Thread.current[:pwn_loop_t0]
616
+ return false unless t0
617
+
618
+ (Time.now - t0) < (hours * 3600)
619
+ rescue StandardError
620
+ false
621
+ end
622
+
595
623
  private_class_method def self.may_finalize?(opts = {})
596
624
  return false if incomplete_final?(text: opts[:text], last_iter: false)
597
625
  return false if request_unsatisfied?(
@@ -1589,23 +1617,13 @@ module PWN
1589
1617
  # Uses TaskSummarizer.active_task_prompt (full plan_context on first
1590
1618
  # force, compact focus thereafter). No-ops when already injected.
1591
1619
  private_class_method def self.inject_task_focus!(opts = {})
1592
- state = opts[:state]
1593
- messages = opts[:messages]
1594
- return nil unless state.is_a?(Hash) && messages.is_a?(Array)
1595
- return nil unless defined?(TaskSummarizer) && TaskSummarizer.enabled?
1596
- return nil if respond_to?(:needs_host_work?) && !needs_host_work?(request: opts[:request] || state[:original_request] || state[:request])
1597
- return nil unless TaskSummarizer.plan_open?(state: state, messages: messages)
1598
-
1599
- req = opts[:request]
1600
- req = state[:original_request] || state[:request] if req.to_s.strip.empty? && state.is_a?(Hash)
1601
- text =
1602
- (TaskSummarizer.active_task_prompt(state: state, force: opts[:force], request: req) if TaskSummarizer.respond_to?(:active_task_prompt))
1603
- return nil if text.to_s.strip.empty?
1604
-
1605
- messages << { role: 'user', content: text }
1606
- text
1607
- rescue StandardError => e
1608
- warn "[pwn-ai/loop] inject_task_focus! swallowed: #{e.class}: #{e.message}"
1620
+ # Original request is the only model-facing goal. TaskSummarizer
1621
+ # remains TUI (emit_plan / about_to). Do not append a compass
1622
+ # user message that can replace the operator ask.
1623
+ return nil if opts.is_a?(Hash)
1624
+
1625
+ nil
1626
+ rescue StandardError
1609
1627
  nil
1610
1628
  end
1611
1629
 
@@ -2396,6 +2414,7 @@ module PWN
2396
2414
  Thread.current[:pwn_request_intent] = intent
2397
2415
  Thread.current[:pwn_extinguished] = {}
2398
2416
  Thread.current[:pwn_same_payload] = Hash.new(0)
2417
+ Thread.current[:pwn_loop_t0] = Time.now unless nested
2399
2418
  debug_progress(msg: "intent=#{intent} engine=#{engine}", debug: opts[:debug])
2400
2419
  expose_current_session(session_id: session_id)
2401
2420
  Mistakes.check_user_correction(request: request, session_id: session_id) if defined?(Mistakes)
@@ -2795,7 +2814,8 @@ module PWN
2795
2814
  iter: i,
2796
2815
  tools_called: tools_called,
2797
2816
  engine_s: engine_s,
2798
- final_chars: final_chars
2817
+ final_chars: final_chars,
2818
+ nested: nested
2799
2819
  )
2800
2820
  TurnFinalizer.leave_user_path! if defined?(TurnFinalizer)
2801
2821
  end
@@ -328,6 +328,7 @@ module PWN
328
328
  source = :injected
329
329
  else
330
330
  tasks = llm_decompose(goal: goal, llm_tasks: opts[:llm_tasks], has_llm_tasks: opts.key?(:llm_tasks))
331
+ tasks = reject_scaffold_tasks(tasks: tasks)
331
332
  source = tasks.any? ? :llm : nil
332
333
  if tasks.length < MIN_PLAN_TASKS
333
334
  tasks = fallback_decompose(goal: goal)
@@ -677,19 +678,9 @@ module PWN
677
678
  return tasks
678
679
  end
679
680
 
680
- tasks << 'Understand the request'
681
- tasks << 'Carry out the core work'
682
-
683
- if goal_lc.match?(/\b(json|ya?ml|table|csv|tsv)\b/)
684
- fmt = goal_lc[/\b(json|ya?ml|table|csv|tsv)\b/]
685
- tasks << "Present the results in #{fmt} format"
686
- elsif goal_lc.match?(/\b(display|show|print|output|format|present|report|export)\b/)
687
- tasks << 'Present the final results in the requested format'
688
- end
689
-
690
- # Only when the user actually asked about tests/lint — not bare "verify".
691
- tasks << 'Run specs, rubocop, and/or rake to verify' if goal_lc.match?(/\b(test|spec|rubocop|rake|lint)\b/) &&
692
- goal_lc.match?(%r{\b(/opt/pwn|code|patch|refactor|commit)\b})
681
+ shaped = request_clause_tasks(goal: goal_text)
682
+ return shaped if shaped.length >= MIN_PLAN_TASKS
683
+ return [goal_text] unless goal_text.strip.empty?
693
684
 
694
685
  tasks
695
686
  rescue StandardError
@@ -706,6 +697,20 @@ module PWN
706
697
  fallback_decompose(goal: opts[:goal])
707
698
  end
708
699
 
700
+ private_class_method def self.request_clause_tasks(opts = {})
701
+ goal = opts[:goal].to_s.gsub(/\s+/, ' ').strip
702
+ return [] if goal.empty?
703
+
704
+ parts = goal.split(/(?<=[.!?])\s+|(?<=;)\s+|\s+(?:ensuring|then|and then)\s+/i)
705
+ parts = parts.map { |p| p.gsub(/\s+/, ' ').strip.sub(/\A(?:so|and|then)\s+/i, '') }
706
+ parts = reject_scaffold_tasks(tasks: parts).reject { |p| p.length < 24 }
707
+ return parts.first(8) if parts.length >= MIN_PLAN_TASKS
708
+
709
+ [goal]
710
+ rescue StandardError
711
+ []
712
+ end
713
+
709
714
  private_class_method def self.truncate_goal(opts = {})
710
715
  # Task summaries are displayed in full — do not ellipsize goals or
711
716
  # plan items. (:len retained for call-site compatibility.)
@@ -987,14 +992,7 @@ module PWN
987
992
  state = opts[:state]
988
993
  request = opts[:request].to_s
989
994
  request = state[:request].to_s if request.empty? && state.is_a?(Hash)
990
- parts = []
991
- if state.is_a?(Hash)
992
- info = active_task(state: state)
993
- parts << info[:item] if info && !info[:item].to_s.empty?
994
- Array(state[:plan]).each { |t| parts << t.to_s }
995
- end
996
- parts << request unless request.empty?
997
- parts.map { |p| p.to_s.gsub(/\s+/, ' ').strip }.reject(&:empty?).uniq.join(' ')
995
+ request.gsub(/\s+/, ' ').strip
998
996
  rescue StandardError
999
997
  opts[:request].to_s
1000
998
  end
@@ -1081,6 +1079,8 @@ module PWN
1081
1079
  return true if s.empty?
1082
1080
  return true if s.match?(/\A(?:GOAL|PLAN|REQUEST|ANSWER|FLAW|PATCH)\s*:/i)
1083
1081
  return true if s.match?(/\A\w+\s+command\s*=/i)
1082
+ return true if s.match?(/\Aunderstand the request\z/i)
1083
+ return true if s.match?(/\Acarry out the core work(?:\s+for:.*)?\z/i)
1084
1084
 
1085
1085
  false
1086
1086
  rescue StandardError
@@ -1393,7 +1393,7 @@ module PWN
1393
1393
  # Soft "verify the result and report" is a closer, not a test runner.
1394
1394
  return :present if s.match?(/\bverif\w*\b/) && !s.match?(/\b(rspec|rubocop|rake|lint|spec|test)\b/)
1395
1395
  return :mutate if s.match?(
1396
- /\b(implement\w*|fix|patch\w*|chang\w*|improv\w*|write|apply|wire|refactor\w*)\b/
1396
+ /\b(implement\w*|fix|patch\w*|chang\w*|improv\w*|write|apply|wire|refactor\w*|core work|requested duration|eligible issues)\b/
1397
1397
  )
1398
1398
  return :discover if s.match?(
1399
1399
  /\b(locat\w*|find|read|inspect|recon\w*|understand|decompos\w*|map|identif\w*|gather|discover|enumerat\w*|scan|probe|determin\w*|analy[sz]e|analysis|root cause|where and why|track|navigat\w*|browse|goto)\b/
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.701'
4
+ VERSION = '0.5.702'
5
5
  end
@@ -51,27 +51,28 @@ describe PWN::AI::Agent::Loop do # rubocop:disable Metrics/BlockLength
51
51
  end
52
52
  end
53
53
 
54
- it 'injects English task focus into model messages (task-as-primary)' do
54
+ it 'does not inject English task focus as a competing user goal' do
55
55
  src = File.read(described_class.method(:run).source_location.first)
56
56
  expect(src).to match(/inject_task_focus!/)
57
- expect(src).to match(/active_task_prompt/)
58
- # inject after emit_plan and inside the iteration loop
59
- expect(src.scan('inject_task_focus!').length).to be >= 3
57
+ req = 'using hping3 what live hosts can you find in this subnet?'
58
+ focus = File.read(described_class.method(:inject_task_focus!).source_location.first)
59
+ expect(focus).to match(/original request is the only/)
60
+ st = { plan: ['Carry out the core work'], plan_idx: 0, original_request: req }
61
+ msgs = [{ role: 'user', content: req }]
62
+ out = described_class.send(:inject_task_focus!, state: st, messages: msgs, request: req, force: true)
63
+ expect(out).to eq nil
64
+ expect(msgs.length).to eq 1
60
65
  end
61
66
 
62
67
  it 'does not keep injecting English focus after the plan is covered' do
63
68
  src = File.read(described_class.method(:inject_task_focus!).source_location.first)
64
- focus = src[/private_class_method def self\.inject_task_focus!.*?private_class_method def self\.\w+/m]
65
- focus ||= src
66
- expect(focus).to match(/plan_open\?/)
69
+ expect(src).to match(/original request is the only/)
67
70
  end
68
71
 
69
72
  it 'does not tell an open English plan to stop after 3 tools just because budget is hot' do
70
73
  src = File.read(described_class.method(:run).source_location.first)
71
74
  expect(src).to match(/budget_exhaustion_hot\?/)
72
- expect(src).to match(/plan_open\?/)
73
- # local ≤3-tool abort is only for a closed/short plan, not mid-goal.
74
- expect(src).to match(/english_open|plan_open\?/)
75
+ expect(src).to match(/original request is the completion signal/i)
75
76
  end
76
77
 
77
78
  it 'parks stale extra budget scars even while the host is hot' do
@@ -97,12 +98,15 @@ describe PWN::AI::Agent::Loop do # rubocop:disable Metrics/BlockLength
97
98
  FileUtils.remove_entry(tmp) if tmp && Dir.exist?(tmp)
98
99
  end
99
100
 
100
- it 're-ranks Registry tools from English tangible tasks after plan (sole driver)' do
101
+ it 'ranks Registry tools from the original request, not the English plan' do
101
102
  src = File.read(described_class.method(:run).source_location.first)
102
103
  expect(src).to match(/TaskSummarizer\.relevance_query/)
103
- expect(src).to match(/relevance_query\(state: ts_state/)
104
- # Must rebind tools after task_summary_plan! (not only from bare request)
105
- expect(src).to match(/task_summary_plan!.*relevance_query|relevance_query.*inject_task_focus!/m)
104
+ q = PWN::AI::Agent::TaskSummarizer.relevance_query(
105
+ state: { plan: ['Carry out the core work', 'Present the result'], request: 'inventory sockets' },
106
+ request: 'inventory sockets'
107
+ )
108
+ expect(q).to eq 'inventory sockets'
109
+ expect(q).not_to match(/core work/i)
106
110
  end
107
111
 
108
112
  it 'P17 evidence_enough does not early-final on bare success while plan open' do
@@ -885,6 +889,27 @@ describe PWN::AI::Agent::Loop do # rubocop:disable Metrics/BlockLength
885
889
  expect(described_class.send(:incomplete_final?, text: poc)).to eq(false)
886
890
  end
887
891
 
892
+ it 'keeps an N-hour host-work request unsatisfied until that duration elapses' do
893
+ req = 'perform unauthenticated analysis for Critical issues for the next eight hours. Eight hours non-stop is a requirement.'
894
+ recap = 'Alternative executed via pwn_eval. Probed unauth GraphQL mutations; wrote graphql/alt.json. Notable: createPriorAuthSupportUploadURL still reachable.'
895
+ msgs = [
896
+ { role: 'user', content: req },
897
+ {
898
+ role: 'assistant',
899
+ tool_calls: [{ function: { name: 'pwn_eval', arguments: '{"code":"1"}' } }]
900
+ },
901
+ { role: 'tool', name: 'pwn_eval', content: '{"success":true,"result":{"value":"1"},"effect":"eval"}' },
902
+ { role: 'assistant', content: recap }
903
+ ]
904
+ Thread.current[:pwn_loop_t0] = Time.now
905
+ expect(described_class.send(:request_unsatisfied?, request: req, messages: msgs)).to eq(true)
906
+ expect(described_class.send(:may_finalize?, request: req, messages: msgs, text: recap)).to eq(false)
907
+ Thread.current[:pwn_loop_t0] = Time.now - (9 * 3600)
908
+ expect(described_class.send(:request_unsatisfied?, request: req, messages: msgs)).to eq(false)
909
+ ensure
910
+ Thread.current[:pwn_loop_t0] = nil
911
+ end
912
+
888
913
  it 'forces tool_choice required on host-work before any tool result, for every engine' do
889
914
  src = File.read(described_class.method(:run).source_location.first)
890
915
  expect(src).to match(/tool_choice/)
@@ -416,10 +416,11 @@ describe PWN::AI::Agent::TaskSummarizer do
416
416
  expect(described_class).not_to receive(:chat_for_plan)
417
417
  tasks = described_class.plan(request: subnet_req)
418
418
  expect(tasks).to be_a(Array)
419
- expect(tasks.length).to be >= 2
419
+ expect(tasks.length).to be >= 1
420
420
  joined = tasks.join(' | ').downcase
421
- # Generic fallback — understand + core work + present JSON — NOT ARP/ICMP canned list.
422
- expect(joined).to match(/json|present|core work|understand|carry out/)
421
+ expect(joined).to match(/subnet|host|json/)
422
+ expect(tasks).not_to include('Understand the request')
423
+ expect(tasks).not_to include('Carry out the core work')
423
424
  expect(described_class.heuristic_decompose(goal: subnet_req)).to eq(
424
425
  described_class.fallback_decompose(goal: subnet_req)
425
426
  )
@@ -695,11 +696,10 @@ describe PWN::AI::Agent::TaskSummarizer do
695
696
  expect(described_class.tool_jargon_task?(item: 'fix the truncation bug')).to eq false
696
697
  end
697
698
 
698
- it 'relevance_query prefers active English task + plan over bare request' do
699
+ it 'relevance_query is the original request, not the English plan' do
699
700
  q = described_class.relevance_query(state: state, request: state[:request])
700
- expect(q).to include('locate the TaskSummarizer source')
701
- expect(q).to include('fix the truncation bug')
702
- expect(q).to include('run rspec to verify')
701
+ expect(q).to eq state[:request].to_s.gsub(/\s+/, ' ').strip
702
+ expect(q).not_to include('Carry out the core work')
703
703
  end
704
704
 
705
705
  it 'apply_prm_advancement! holds on +1 search streak and on -1; advances on next-task handoff' do
@@ -981,14 +981,53 @@ describe PWN::AI::Agent::TaskSummarizer do
981
981
  expect(st[:plan_idx]).to eq 1
982
982
  end
983
983
 
984
+ it 'does not complete Carry out the core work on three directory listings' do
985
+ st = described_class.fresh(request: 'perform unauthenticated analysis for eight hours')
986
+ st[:plan] = [
987
+ 'Understand the request',
988
+ 'Carry out the core work',
989
+ 'Present the result and report completion'
990
+ ]
991
+ st[:plan_idx] = 1
992
+ 3.times do |i|
993
+ described_class.record!(
994
+ state: st,
995
+ name: 'shell',
996
+ args: { 'command' => "cat /opt/bugbounty/programs/curative/POLICY.md #{i}" },
997
+ result: '{"success":true,"result":{"stdout":"Curative Inc. looks forward to working with the security community to find security vulnerabilities. POLICY.md body easily over forty characters.","exit":0}}'
998
+ )
999
+ end
1000
+ expect(st[:plan_idx]).to eq 1
1001
+ end
1002
+
984
1003
  it 'does not paste the full operator goal into fallback understand/carry-out tasks' do
985
1004
  goal = 'Until we can claim credentials perform unauthenticated analysis for Critical / High severity issues ' \
986
1005
  'eligible for submission leveraging ~/.pwn/skills for all subdomains in scope for the next eight hours'
987
1006
  tasks = described_class.fallback_decompose(goal: goal)
988
- expect(tasks.length).to be >= 2
1007
+ expect(tasks.length).to be >= 1
989
1008
  expect(tasks.grep(/Understand the request:/)).to eq([])
990
1009
  expect(tasks.grep(/Carry out the core work for:/)).to eq([])
991
- expect(tasks.join("\n").length).to be < goal.length
1010
+ expect(tasks).not_to include('Understand the request')
1011
+ expect(tasks).not_to include('Carry out the core work')
1012
+ joined = tasks.join("\n")
1013
+ expect(joined).to match(/unauth/i)
1014
+ expect(joined).to match(/critical|high/i)
1015
+ expect(joined).to match(/eight hours|requested duration/i)
1016
+ expect(joined).to match(/PoC|severity|attack chain/i)
1017
+ end
1018
+
1019
+ it 'keeps distinctive tokens from unrelated unique goals instead of a stub plan' do
1020
+ [
1021
+ 'Inventory every listening TCP socket on this host and print pid user and port',
1022
+ 'Name every Ruby constant under PWN::AI::Agent and print them as a JSON array'
1023
+ ].each do |goal|
1024
+ tasks = described_class.fallback_decompose(goal: goal)
1025
+ expect(tasks).not_to include('Understand the request')
1026
+ expect(tasks).not_to include('Carry out the core work')
1027
+ joined = tasks.join(' ')
1028
+ tokens = goal.scan(%r{[A-Za-z0-9_./-]{5,}}).uniq
1029
+ expect(tokens.count { |tok| joined.downcase.include?(tok.downcase) }).to be >= 2
1030
+ end
992
1031
  end
993
1032
 
994
1033
  it 'a verify task is covered after the verifier ran, even with remaining offenses' do
@@ -338,6 +338,7 @@
338
338
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.default_interactive_toolsets Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.default_interactive_toolsets`: "}]}
339
339
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.degrade_text_only Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.degrade_text_only`: "}]}
340
340
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.dispatch_fail_n Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.dispatch_fail_n`: "}]}
341
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.duration_unsatisfied? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.duration_unsatisfied?`: "}]}
341
342
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.emit_task_summary Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.emit_task_summary`: "}]}
342
343
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.engine_transient? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.engine_transient?`: "}]}
343
344
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.escalate Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.escalate`: "}]}
@@ -700,6 +701,7 @@
700
701
  {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.reject_scaffold_tasks Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.reject_scaffold_tasks`: "}]}
701
702
  {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.relevance_query Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.relevance_query`: Supported Method Parameters\n\nq = PWN::AI::Agent::TaskSummarizer.relevance_query(\n\nstate: 'optional - fresh() hash',\nrequest: 'optional - original user goal fallback'\n\n)\n"}]}
702
703
  {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.remember_brief! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.remember_brief!`: "}]}
704
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.request_clause_tasks Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.request_clause_tasks`: "}]}
703
705
  {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.sidecar_timeout Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.sidecar_timeout`: "}]}
704
706
  {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.squeeze_request_ws Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.squeeze_request_ws`: "}]}
705
707
  {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.task_complete_enough? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.task_complete_enough?`: "}]}
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.701
4
+ version: 0.5.702
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.