pwn 0.5.659 → 0.5.660

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: 1cab86775d3b1611d7b0aadf6bd7ceb11ba1e673d239ff4b141bbf29a0212217
4
- data.tar.gz: 1833f2e8bff11bbeedfc059734c99d3b1743291795829a65d24066e8319d8272
3
+ metadata.gz: '06678f228c6acc69d9fb173f01f3825af6a7a8dcffcb8ed24c865bf281e0c65c'
4
+ data.tar.gz: 9967223f974d122e30cbb4be7aea369108e28b951ace091a1ed90d54825a4720
5
5
  SHA512:
6
- metadata.gz: 44a37c7080857da5ca07df39e9bb4856d8c30c621b77e8348be7f4257afdcb5c05bd4d0c4816b67b20415c5e22c80d44b4bf276823b02c7028067c4bdbae95bd
7
- data.tar.gz: 2ddbac637da6b33a5f96e45d58f9210ec843fd6077b5815ccd1698d13143d7ec645aa2a7c2dc2a23a84b93e0d1cc4498283119565d61152af49805704b4ab58e
6
+ metadata.gz: 6feb604302f7e6fd428e8dcd2d1c6a888ed2e6d4c96f3e7b75b6537a5694c3053da6a69da751275c8c168f627f6b71b031ed502cf02b1b0eb8bb2dfd72e2e354
7
+ data.tar.gz: 26f65f4e16312694c1163b0de7fe0545ca8ad7b8e1cdea647d931f131de6aaef89f0fe8b39be72509932295c1ad1521066c88722d5654cb6a856891c1c19e860
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PWN
4
+ module AI
5
+ module Agent
6
+ # Coalesce a burst of tool calls into periodic
7
+ # "summary_of_current_task" UI lines without stopping the loop.
8
+ module TaskSummarizer
9
+ DEFAULT_EVERY = 5
10
+ DEFAULT_INTERVAL_S = 8.0
11
+ MAX_BUFFER = 64
12
+
13
+ module_function
14
+
15
+ def enabled?
16
+ v = PWN::Env.dig(:ai, :agent, :task_summary)
17
+ v.nil? || !!v
18
+ rescue StandardError
19
+ true
20
+ end
21
+
22
+ def verbose?
23
+ !!PWN::Env.dig(:ai, :agent, :task_summary_verbose)
24
+ rescue StandardError
25
+ false
26
+ end
27
+
28
+ def every_n
29
+ n = PWN::Env.dig(:ai, :agent, :task_summary_every)
30
+ n = DEFAULT_EVERY if n.nil?
31
+ [n.to_i, 1].max
32
+ rescue StandardError
33
+ DEFAULT_EVERY
34
+ end
35
+
36
+ def interval_s
37
+ t = PWN::Env.dig(:ai, :agent, :task_summary_interval_s)
38
+ t = DEFAULT_INTERVAL_S if t.nil?
39
+ [t.to_f, 1.0].max
40
+ rescue StandardError
41
+ DEFAULT_INTERVAL_S
42
+ end
43
+
44
+ # Per-run state (also safe for nested/swarm if callers keep their own hash)
45
+ def fresh(opts = {})
46
+ {
47
+ request: opts[:request].to_s,
48
+ events: [],
49
+ since_emit: 0,
50
+ last_emit_at: Time.now,
51
+ total: 0,
52
+ counts: Hash.new(0)
53
+ }
54
+ end
55
+
56
+ def record!(state, name, args, result)
57
+ return nil unless state
58
+
59
+ preview = args.is_a?(String) ? args.to_s[0, 60] : args.inspect[0, 60]
60
+ rs = result.to_s
61
+ ok = !rs.match?(/\A\s*\{?\s*"?(success|ok)"?\s*=>\s*false/i) &&
62
+ !rs.match?(/ERROR:|Traceback|NoMethodError|StandardError/i)
63
+ state[:events] << { name: name.to_s, preview: preview, ok: ok, t: Time.now }
64
+ state[:events].shift while state[:events].size > MAX_BUFFER
65
+ state[:counts][name.to_s] += 1
66
+ state[:total] += 1
67
+ state[:since_emit] += 1
68
+
69
+ due = state[:since_emit] >= every_n ||
70
+ (Time.now - state[:last_emit_at]) >= interval_s
71
+ due ? emit!(state) : nil
72
+ end
73
+
74
+ def emit!(state, final: false)
75
+ return nil if state.nil? || state[:events].empty?
76
+
77
+ counts = state[:counts].sort_by { |_, c| -c }.map { |n, c| "#{n}×#{c}" }
78
+ recent = state[:events].last(every_n)
79
+ focus = recent.map { |e| e[:name] }.uniq.first(4).join(', ')
80
+ fails = state[:events].count { |e| !e[:ok] }
81
+ tail = recent.map { |e| e[:preview] }.compact.reject(&:empty?).last
82
+ bit = tail && !tail.empty? ? "; e.g. #{tail}" : ''
83
+ fail_bit = fails.positive? ? "; failures=#{fails}" : ''
84
+ phase = final ? 'done' : 'in progress'
85
+ state[:since_emit] = 0
86
+ state[:last_emit_at] = Time.now
87
+ "#{phase}: #{focus} — #{state[:total]} tools (#{counts.first(6).join(', ')})#{fail_bit}#{bit}"
88
+ end
89
+
90
+ def flush!(state)
91
+ emit!(state, final: true)
92
+ end
93
+
94
+ # Author(s):: 0day Inc. <support@0dayinc.com>
95
+
96
+ public_class_method def self.authors
97
+ "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n"
98
+ end
99
+
100
+ # Display Usage for this Module
101
+
102
+ public_class_method def self.help
103
+ puts <<~USAGE
104
+ USAGE:
105
+ state = PWN::AI::Agent::TaskSummarizer.fresh(request: 'do the thing')
106
+ line = PWN::AI::Agent::TaskSummarizer.record!(state, 'shell', 'ls', '{success:true}')
107
+ line = PWN::AI::Agent::TaskSummarizer.flush!(state)
108
+ PWN::AI::Agent::TaskSummarizer.enabled?
109
+ PWN::AI::Agent::TaskSummarizer.verbose?
110
+ PWN::AI::Agent::TaskSummarizer.every_n
111
+ PWN::AI::Agent::TaskSummarizer.interval_s
112
+
113
+ #{self}.authors
114
+ USAGE
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
data/lib/pwn/ai/agent.rb CHANGED
@@ -31,6 +31,7 @@ module PWN
31
31
  autoload :Swarm, 'pwn/ai/agent/swarm'
32
32
  autoload :Reward, 'pwn/ai/agent/reward'
33
33
  autoload :Curriculum, 'pwn/ai/agent/curriculum'
34
+ autoload :TaskSummarizer, 'pwn/ai/agent/task_summarizer'
34
35
 
35
36
  # Display a List of Every PWN::AI Module
36
37
 
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.659'
4
+ VERSION = '0.5.660'
5
5
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe PWN::AI::Agent::TaskSummarizer do
6
+ it 'fresh method should exist' do
7
+ expect(described_class).to respond_to :fresh
8
+ end
9
+
10
+ it 'record! method should exist' do
11
+ expect(described_class).to respond_to :record!
12
+ end
13
+
14
+ it 'emit! method should exist' do
15
+ expect(described_class).to respond_to :emit!
16
+ end
17
+
18
+ it 'flush! method should exist' do
19
+ expect(described_class).to respond_to :flush!
20
+ end
21
+
22
+ it 'enabled? method should exist' do
23
+ expect(described_class).to respond_to :enabled?
24
+ end
25
+
26
+ it 'should display information for authors' do
27
+ expect(described_class).to respond_to :authors
28
+ end
29
+
30
+ it 'should display information for existing help method' do
31
+ expect(described_class).to respond_to :help
32
+ end
33
+
34
+ it 'coalesces tool bursts into a summary line' do
35
+ allow(PWN::Env).to receive(:dig).and_call_original
36
+ allow(PWN::Env).to receive(:dig).with(:ai, :agent, :task_summary_every).and_return(2)
37
+ allow(PWN::Env).to receive(:dig).with(:ai, :agent, :task_summary_interval_s).and_return(9_999)
38
+ allow(PWN::Env).to receive(:dig).with(:ai, :agent, :task_summary).and_return(true)
39
+ allow(PWN::Env).to receive(:dig).with(:ai, :agent, :task_summary_verbose).and_return(false)
40
+
41
+ state = described_class.fresh(request: 'fix rake')
42
+ expect(described_class.record!(state, 'shell', 'rake', '{success:true}')).to be_nil
43
+ line = described_class.record!(state, 'shell', 'rake 2>&1', '{success:true}')
44
+ expect(line).to be_a(String)
45
+ expect(line).to include('in progress')
46
+ expect(line).to include('shell')
47
+ expect(state[:total]).to eq 2
48
+
49
+ fin = described_class.flush!(state)
50
+ expect(fin).to include('done')
51
+ expect(fin).to include('2 tools')
52
+ end
53
+ end
@@ -460,6 +460,16 @@
460
460
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Swarm.retire Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Swarm.retire`: Supported Method Parameters\n\nPWN::AI::Agent::Swarm.retire(name: ‘required - persona name’)\n"}]}
461
461
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Swarm.spawn Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Swarm.spawn`: Supported Method Parameters\n\nPWN::AI::Agent::Swarm.spawn(\n\nname: 'required - persona name (snake_case)',\nrole: 'required - system_role_content overlay for this persona',\ntoolsets: 'optional - Array of Registry toolset names',\nengine: 'optional - :openai / :anthropic / :grok / :gemini / :ollama',\nmax_iters: 'optional - per-turn iteration cap for this persona'\n\n)\n"}]}
462
462
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Swarm.with_persona_env Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Swarm.with_persona_env`: "}]}
463
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.authors Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
464
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.help Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.help`: "}]}
465
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.emit! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.emit!`: "}]}
466
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.enabled? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.enabled?`: "}]}
467
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.every_n Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.every_n`: "}]}
468
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.flush! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.flush!`: "}]}
469
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.fresh Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.fresh`: "}]}
470
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.interval_s Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.interval_s`: "}]}
471
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.record! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.record!`: "}]}
472
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::TaskSummarizer.verbose? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TaskSummarizer.verbose?`: "}]}
463
473
  {"messages":[{"role":"user","content":"PWN::AI::Agent::TransparentBrowser.analyze Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TransparentBrowser.analyze`: Supported Method Parameters\n\nai_analysis = PWN::AI::Agent::TransparentBrowser.analyze(\n\nrequest: 'required - current step in the JavaScript debugging session to analyze',\nsource_to_review: 'required - the block of JavaScript code in which the current step resides'\n\n)\n"}]}
464
474
  {"messages":[{"role":"user","content":"PWN::AI::Agent::TransparentBrowser.authors Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TransparentBrowser.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
465
475
  {"messages":[{"role":"user","content":"PWN::AI::Agent::TransparentBrowser.help Usage"},{"role":"assistant","content":"`PWN::AI::Agent::TransparentBrowser.help`: "}]}
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.659
4
+ version: 0.5.660
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.
@@ -1856,6 +1856,7 @@ files:
1856
1856
  - lib/pwn/ai/agent/reward.rb
1857
1857
  - lib/pwn/ai/agent/sast.rb
1858
1858
  - lib/pwn/ai/agent/swarm.rb
1859
+ - lib/pwn/ai/agent/task_summarizer.rb
1859
1860
  - lib/pwn/ai/agent/tools/cron.rb
1860
1861
  - lib/pwn/ai/agent/tools/curriculum.rb
1861
1862
  - lib/pwn/ai/agent/tools/extrospection.rb
@@ -2317,6 +2318,7 @@ files:
2317
2318
  - spec/lib/pwn/ai/agent/reward_spec.rb
2318
2319
  - spec/lib/pwn/ai/agent/sast_spec.rb
2319
2320
  - spec/lib/pwn/ai/agent/swarm_spec.rb
2321
+ - spec/lib/pwn/ai/agent/task_summarizer_spec.rb
2320
2322
  - spec/lib/pwn/ai/agent/tools/cron_spec.rb
2321
2323
  - spec/lib/pwn/ai/agent/tools/curriculum_spec.rb
2322
2324
  - spec/lib/pwn/ai/agent/tools/extrospection_spec.rb