pwn 0.5.663 → 0.5.665
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/curriculum.rb +12 -4
- data/lib/pwn/ai/agent/learning.rb +443 -14
- data/lib/pwn/ai/agent/loop.rb +25 -5
- data/lib/pwn/ai/agent/mistakes.rb +124 -6
- data/lib/pwn/ai/agent/tools/learning.rb +22 -0
- data/lib/pwn/ai/agent/tools/memory.rb +29 -4
- data/lib/pwn/ai/agent/tools/mistakes.rb +22 -0
- data/lib/pwn/ai/agent/tools/sessions.rb +37 -2
- data/lib/pwn/memory.rb +105 -6
- data/lib/pwn/plugins/repl.rb +2 -2
- data/lib/pwn/sessions.rb +268 -4
- data/lib/pwn/version.rb +1 -1
- data/spec/integration/persistence_roundtrip_spec.rb +2 -2
- data/spec/lib/pwn/ai/agent/lean_stores_spec.rb +269 -0
- data/spec/lib/pwn/ai/agent/learning_spec.rb +137 -0
- data/spec/lib/pwn/ai/agent/loop_spec.rb +42 -0
- data/spec/lib/pwn/ai/agent/mistakes_spec.rb +75 -0
- data/spec/lib/pwn/memory_spec.rb +46 -1
- data/spec/lib/pwn/sessions_spec.rb +40 -1
- data/third_party/pwn_rdoc.jsonl +21 -3
- metadata +2 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'spec_helper'
|
|
4
4
|
require 'tmpdir'
|
|
5
|
+
require 'fileutils'
|
|
5
6
|
|
|
6
7
|
describe PWN::AI::Agent::Learning do
|
|
7
8
|
it 'should display information for authors' do
|
|
@@ -86,4 +87,140 @@ describe PWN::AI::Agent::Learning do
|
|
|
86
87
|
ensure
|
|
87
88
|
FileUtils.rm_rf(tmp) if defined?(tmp) && tmp
|
|
88
89
|
end
|
|
90
|
+
|
|
91
|
+
it 'P29 resyncs verdict after critic floor and strips request envelopes in to_context' do
|
|
92
|
+
tmp = Dir.mktmpdir
|
|
93
|
+
stub_const('PWN::AI::Agent::Learning::LEARNING_FILE', File.join(tmp, 'learning.jsonl'))
|
|
94
|
+
stub_const('PWN::Memory::MEMORY_FILE', File.join(tmp, 'memory.json'))
|
|
95
|
+
|
|
96
|
+
PWN::AI::Agent::Learning.reset
|
|
97
|
+
|
|
98
|
+
# Critic-capped solved-at-0.3 pattern (pre-P29 poison)
|
|
99
|
+
PWN::AI::Agent::Learning.note_outcome(
|
|
100
|
+
task: "REQUEST:\nwhats the bottom line?\n\nANSWER:\nfake bottom line",
|
|
101
|
+
success: false,
|
|
102
|
+
score: 0.3,
|
|
103
|
+
details: 'solved(0.3) heuristic overlap=0.75 ratio=1.0 | fake bottom line',
|
|
104
|
+
tags: %w[auto loop solved plan_cover_high]
|
|
105
|
+
)
|
|
106
|
+
PWN::AI::Agent::Learning.note_outcome(
|
|
107
|
+
task: 'real bare goal that failed',
|
|
108
|
+
success: false,
|
|
109
|
+
score: 0.22,
|
|
110
|
+
details: 'wrong(0.22) heuristic overlap=0.06 | blew up',
|
|
111
|
+
tags: %w[auto loop wrong]
|
|
112
|
+
)
|
|
113
|
+
PWN::AI::Agent::Learning.note_outcome(
|
|
114
|
+
task: 'successful scan',
|
|
115
|
+
success: true,
|
|
116
|
+
score: 0.9,
|
|
117
|
+
details: 'solved(0.9) ok',
|
|
118
|
+
tags: %w[auto loop solved]
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# reconcile should flip solved→partial on the 0.3 row
|
|
122
|
+
rep = PWN::AI::Agent::Learning.reconcile_verdict_tags!
|
|
123
|
+
expect(rep[:repaired]).to be >= 1
|
|
124
|
+
|
|
125
|
+
rows = PWN::AI::Agent::Learning.outcomes(limit: 20)
|
|
126
|
+
fixed = rows.find { |r| (r[:score].to_f - 0.3).abs < 0.001 }
|
|
127
|
+
expect(Array(fixed[:tags])).to include('partial')
|
|
128
|
+
expect(Array(fixed[:tags])).not_to include('solved')
|
|
129
|
+
expect(fixed[:details]).to match(/\Apartial\(0\.30?\)/)
|
|
130
|
+
|
|
131
|
+
ctx = PWN::AI::Agent::Learning.to_context(limit: 5)
|
|
132
|
+
# envelope stripped
|
|
133
|
+
expect(ctx).to include('whats the bottom line?')
|
|
134
|
+
expect(ctx).not_to match(/REQUEST:\nwhats/)
|
|
135
|
+
# score visible
|
|
136
|
+
expect(ctx).to match(/\[0\.30\]/)
|
|
137
|
+
# RECENT OUTCOMES and RECENT FAILURES must not be identical dumps
|
|
138
|
+
# (dedupe by id) — with 1 success + 2 fails, outcomes shows mix
|
|
139
|
+
expect(ctx).to include('successful scan')
|
|
140
|
+
expect(ctx).to include('cause:')
|
|
141
|
+
ensure
|
|
142
|
+
FileUtils.rm_rf(tmp) if defined?(tmp) && tmp
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'P29 verdict_for_score thresholds match Reward.judge' do
|
|
146
|
+
expect(PWN::AI::Agent::Learning.send(:verdict_for_score, score: 0.6)).to eq(:solved)
|
|
147
|
+
expect(PWN::AI::Agent::Learning.send(:verdict_for_score, score: 0.59)).to eq(:partial)
|
|
148
|
+
expect(PWN::AI::Agent::Learning.send(:verdict_for_score, score: 0.3)).to eq(:partial)
|
|
149
|
+
expect(PWN::AI::Agent::Learning.send(:verdict_for_score, score: 0.29)).to eq(:wrong)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it 'note_outcome enforces OUTCOME_DETAILS_MAX' do
|
|
153
|
+
tmp = Dir.mktmpdir
|
|
154
|
+
path = File.join(tmp, 'learning.jsonl')
|
|
155
|
+
stub_const('PWN::AI::Agent::Learning::LEARNING_FILE', path)
|
|
156
|
+
long = 'D' * (PWN::AI::Agent::Learning::OUTCOME_DETAILS_MAX + 100)
|
|
157
|
+
e = PWN::AI::Agent::Learning.note_outcome(task: 'policy details cap', success: true, details: long, tags: %w[spec])
|
|
158
|
+
expect(e[:details].bytesize).to be <= PWN::AI::Agent::Learning::OUTCOME_DETAILS_MAX
|
|
159
|
+
ensure
|
|
160
|
+
FileUtils.rm_rf(tmp) if tmp
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it 'prune_outcomes! keeps gold/high-value and enforces MAX_OUTCOME_ROWS' do
|
|
164
|
+
tmp = Dir.mktmpdir
|
|
165
|
+
path = File.join(tmp, 'learning.jsonl')
|
|
166
|
+
stub_const('PWN::AI::Agent::Learning::LEARNING_FILE', path)
|
|
167
|
+
stub_const('PWN::AI::Agent::Learning::MAX_OUTCOME_ROWS', 30)
|
|
168
|
+
stub_const('PWN::AI::Agent::Learning::EXEMPLARS_POOL_MIN', 5)
|
|
169
|
+
stub_const('PWN::AI::Agent::Learning::FAILURE_WINDOW_MIN', 5)
|
|
170
|
+
stub_const('PWN::AI::Agent::Learning::OUTCOME_RECENT_DAYS', 1)
|
|
171
|
+
stub_const('PWN::AI::Agent::Learning::OUTCOME_RETAIN_DAYS', 2)
|
|
172
|
+
|
|
173
|
+
now = Time.now.utc
|
|
174
|
+
rows = []
|
|
175
|
+
# gold protected
|
|
176
|
+
5.times do |i|
|
|
177
|
+
rows << {
|
|
178
|
+
id: "g#{i}", task: "gold task #{i}", success: true, score: 0.9,
|
|
179
|
+
session_id: "sid_g#{i}", details: 'ok', tags: %w[auto],
|
|
180
|
+
timestamp: (now - (10 * 86_400)).iso8601
|
|
181
|
+
}
|
|
182
|
+
end
|
|
183
|
+
# high-value tag protected
|
|
184
|
+
rows << {
|
|
185
|
+
id: 'hv1', task: 'needs human row', success: false, score: 0.2,
|
|
186
|
+
session_id: 'sid_hv', details: 'x', tags: %w[needs_human],
|
|
187
|
+
timestamp: (now - (20 * 86_400)).iso8601
|
|
188
|
+
}
|
|
189
|
+
# old low-value noise (should be droppable)
|
|
190
|
+
40.times do |i|
|
|
191
|
+
rows << {
|
|
192
|
+
id: "n#{i}", task: "noise #{i}", success: false, score: 0.1,
|
|
193
|
+
session_id: "sid_n#{i}", details: 'noise', tags: %w[auto loop partial],
|
|
194
|
+
timestamp: (now - (30 * 86_400)).iso8601
|
|
195
|
+
}
|
|
196
|
+
end
|
|
197
|
+
File.open(path, 'w') { |f| rows.each { |r| f.puts(JSON.generate(r)) } }
|
|
198
|
+
|
|
199
|
+
res = PWN::AI::Agent::Learning.prune_outcomes!
|
|
200
|
+
kept = File.readlines(path).map { |l| JSON.parse(l, symbolize_names: true) }
|
|
201
|
+
ids = kept.map { |r| r[:id] }
|
|
202
|
+
expect(ids).to include('g0', 'hv1')
|
|
203
|
+
expect(kept.size).to be <= 30
|
|
204
|
+
expect(res[:kept]).to eq(kept.size)
|
|
205
|
+
ensure
|
|
206
|
+
FileUtils.rm_rf(tmp) if tmp
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
it 'consolidate respects MAX_MEMORY_ENTRIES and PROTECT prefixes' do
|
|
210
|
+
tmp = Dir.mktmpdir
|
|
211
|
+
mem_path = File.join(tmp, 'memory.json')
|
|
212
|
+
stub_const('PWN::Memory::MEMORY_FILE', mem_path)
|
|
213
|
+
PWN::Memory.clear(force: true)
|
|
214
|
+
PWN::Memory.remember(key: :operator_pref_keep, value: 'must survive', category: :preference)
|
|
215
|
+
25.times do |i|
|
|
216
|
+
PWN::Memory.remember(key: :"bulk_#{i}", value: "lesson body #{i} unique #{i}", category: :lesson, importance: 0.1, confidence: 0.1)
|
|
217
|
+
end
|
|
218
|
+
res = PWN::AI::Agent::Learning.consolidate(max_entries: 10)
|
|
219
|
+
mem = PWN::Memory.load
|
|
220
|
+
expect(mem.keys).to include(:operator_pref_keep)
|
|
221
|
+
expect(mem.size).to be <= 10
|
|
222
|
+
expect(res[:remaining]).to eq(mem.size)
|
|
223
|
+
ensure
|
|
224
|
+
FileUtils.rm_rf(tmp) if tmp
|
|
225
|
+
end
|
|
89
226
|
end
|
|
@@ -60,6 +60,48 @@ describe PWN::AI::Agent::Loop do
|
|
|
60
60
|
expect(src).to match(/task_summary_plan!.*relevance_query|relevance_query.*inject_task_focus!/m)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
it 'P17 evidence_enough does not early-final on bare success while plan open' do
|
|
64
|
+
# Multi-step English plan mid-flight + tool {"success":true} must NOT
|
|
65
|
+
# force text-only — that blocked legitimate completion.
|
|
66
|
+
loop_mod = described_class
|
|
67
|
+
msgs = [
|
|
68
|
+
{ role: 'user', content: 'fix p17' },
|
|
69
|
+
{ role: 'tool', content: '{"success":true,"result":{"stdout":"a"}}' },
|
|
70
|
+
{ role: 'tool', content: '{"success":true,"result":{"stdout":"b"}}' },
|
|
71
|
+
{ role: 'tool', content: '{"success":true,"result":{"stdout":"c"}}' }
|
|
72
|
+
]
|
|
73
|
+
open_plan = { plan: %w[identify determine inspect implement verify confirm], plan_idx: 2 }
|
|
74
|
+
r_open = loop_mod.send(
|
|
75
|
+
:evidence_enough_to_finalize?,
|
|
76
|
+
messages: msgs, turn_fails: {}, i: 5, max_iters: 40,
|
|
77
|
+
request: 'fix p17', plan_steps: 6, ts_state: open_plan
|
|
78
|
+
)
|
|
79
|
+
expect(r_open).to be false
|
|
80
|
+
|
|
81
|
+
last_plan = { plan: %w[identify implement verify], plan_idx: 2 }
|
|
82
|
+
msgs_mut = msgs + [
|
|
83
|
+
{ role: 'tool', content: '{"success":true,"result":{"stdout":"0 offenses detected"}}' }
|
|
84
|
+
]
|
|
85
|
+
r_mut = loop_mod.send(
|
|
86
|
+
:evidence_enough_to_finalize?,
|
|
87
|
+
messages: msgs_mut, turn_fails: {}, i: 5, max_iters: 40,
|
|
88
|
+
request: 'fix p17', plan_steps: 3, ts_state: last_plan
|
|
89
|
+
)
|
|
90
|
+
expect(r_mut).to be true
|
|
91
|
+
|
|
92
|
+
r_bare = loop_mod.send(
|
|
93
|
+
:evidence_enough_to_finalize?,
|
|
94
|
+
messages: msgs, turn_fails: {}, i: 5, max_iters: 40,
|
|
95
|
+
request: 'fix p17', plan_steps: 3, ts_state: last_plan
|
|
96
|
+
)
|
|
97
|
+
expect(r_bare).to be false
|
|
98
|
+
|
|
99
|
+
# call site must pass ts_state
|
|
100
|
+
src = File.read(loop_mod.method(:run).source_location.first)
|
|
101
|
+
expect(src).to match(/evidence_enough_to_finalize\?\([\s\S]*?ts_state: ts_state/)
|
|
102
|
+
expect(src).to match(/English-task gate/)
|
|
103
|
+
end
|
|
104
|
+
|
|
63
105
|
it 'does not put TaskSummarizer into Reward credit paths' do
|
|
64
106
|
# Loop may call TaskSummarizer executive APIs, not Reward.judge from TaskSummarizer
|
|
65
107
|
ts = File.read(PWN::AI::Agent::TaskSummarizer.method(:plan).source_location.first)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'spec_helper'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
require 'fileutils'
|
|
4
6
|
|
|
5
7
|
describe PWN::AI::Agent::Mistakes do
|
|
6
8
|
it 'should display information for authors' do
|
|
@@ -62,4 +64,77 @@ describe PWN::AI::Agent::Mistakes do
|
|
|
62
64
|
expect(got[:structured_fix][:strategy]).to eq 'typo'
|
|
63
65
|
expect(got[:structured_fix][:holdout_tests].length).to eq 2
|
|
64
66
|
end
|
|
67
|
+
|
|
68
|
+
it 'enforces SAMPLE_ARGS_MAX SNIPPET_MAX ERROR_MAX SESSIONS_KEEP on record' do
|
|
69
|
+
tmp = Dir.mktmpdir
|
|
70
|
+
path = File.join(tmp, 'mistakes.json')
|
|
71
|
+
stub_const('PWN::AI::Agent::Mistakes::MISTAKES_FILE', path)
|
|
72
|
+
FileUtils.rm_f(path)
|
|
73
|
+
long_err = 'E' * (PWN::AI::Agent::Mistakes::ERROR_MAX + 200)
|
|
74
|
+
long_args = 'A' * (PWN::AI::Agent::Mistakes::SAMPLE_ARGS_MAX + 80)
|
|
75
|
+
m = nil
|
|
76
|
+
5.times do |i|
|
|
77
|
+
m = PWN::AI::Agent::Mistakes.record(
|
|
78
|
+
tool: 'shell',
|
|
79
|
+
error: long_err,
|
|
80
|
+
args: long_args,
|
|
81
|
+
session_id: "sess_#{i}"
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
expect(m[:snippet].to_s.bytesize).to be <= PWN::AI::Agent::Mistakes::SNIPPET_MAX
|
|
85
|
+
expect(m[:error].to_s.bytesize).to be <= PWN::AI::Agent::Mistakes::ERROR_MAX
|
|
86
|
+
expect(m[:sample_args].to_s.bytesize).to be <= PWN::AI::Agent::Mistakes::SAMPLE_ARGS_MAX
|
|
87
|
+
expect(Array(m[:sessions]).length).to be <= PWN::AI::Agent::Mistakes::SESSIONS_KEEP
|
|
88
|
+
expect(m[:count]).to eq(5)
|
|
89
|
+
ensure
|
|
90
|
+
FileUtils.rm_rf(tmp) if tmp
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'lean! keeps unresolved and REPEATING scars; caps resolved via MAX_RESOLVED_KEPT' do
|
|
94
|
+
tmp = Dir.mktmpdir
|
|
95
|
+
path = File.join(tmp, 'mistakes.json')
|
|
96
|
+
stub_const('PWN::AI::Agent::Mistakes::MISTAKES_FILE', path)
|
|
97
|
+
stub_const('PWN::AI::Agent::Mistakes::MAX_RESOLVED_KEPT', 2)
|
|
98
|
+
stub_const('PWN::AI::Agent::Mistakes::RESOLVED_MIN_AGE_DAYS', 0)
|
|
99
|
+
FileUtils.rm_f(path)
|
|
100
|
+
|
|
101
|
+
open_m = PWN::AI::Agent::Mistakes.record(tool: 'open_tool', error: 'still broken open')
|
|
102
|
+
rep = nil
|
|
103
|
+
3.times { rep = PWN::AI::Agent::Mistakes.record(tool: 'rep_tool', error: 'repeat me') }
|
|
104
|
+
expect(rep[:count]).to be >= PWN::AI::Agent::Mistakes::REPEAT_THRESHOLD
|
|
105
|
+
|
|
106
|
+
resolved_sigs = []
|
|
107
|
+
4.times do |i|
|
|
108
|
+
m = PWN::AI::Agent::Mistakes.record(tool: "done_#{i}", error: "once #{i}")
|
|
109
|
+
PWN::AI::Agent::Mistakes.resolve(signature: m[:signature], fix: "fix #{i}")
|
|
110
|
+
# age resolved_at into the past for drop eligibility
|
|
111
|
+
store = PWN::AI::Agent::Mistakes.load
|
|
112
|
+
store[m[:signature].to_sym][:resolved_at] = (Time.now.utc - (40 * 86_400)).iso8601
|
|
113
|
+
store[m[:signature].to_sym][:count] = 1
|
|
114
|
+
PWN::AI::Agent::Mistakes.save(store: store)
|
|
115
|
+
resolved_sigs << m[:signature]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
res = PWN::AI::Agent::Mistakes.lean!
|
|
119
|
+
store = PWN::AI::Agent::Mistakes.load
|
|
120
|
+
expect(store.keys.map(&:to_s)).to include(open_m[:signature], rep[:signature])
|
|
121
|
+
resolved_left = store.values.count { |e| e[:resolved] && e[:fix].to_s != '' }
|
|
122
|
+
expect(resolved_left).to be <= 2
|
|
123
|
+
expect(res[:remaining]).to be <= store.size
|
|
124
|
+
ensure
|
|
125
|
+
FileUtils.rm_rf(tmp) if tmp
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'resolve clamps fix to FIX_MAX' do
|
|
129
|
+
tmp = Dir.mktmpdir
|
|
130
|
+
path = File.join(tmp, 'mistakes.json')
|
|
131
|
+
stub_const('PWN::AI::Agent::Mistakes::MISTAKES_FILE', path)
|
|
132
|
+
FileUtils.rm_f(path)
|
|
133
|
+
m = PWN::AI::Agent::Mistakes.record(tool: 't', error: 'e')
|
|
134
|
+
long_fix = 'F' * (PWN::AI::Agent::Mistakes::FIX_MAX + 50)
|
|
135
|
+
got = PWN::AI::Agent::Mistakes.resolve(signature: m[:signature], fix: long_fix)
|
|
136
|
+
expect(got[:fix].bytesize).to eq(PWN::AI::Agent::Mistakes::FIX_MAX)
|
|
137
|
+
ensure
|
|
138
|
+
FileUtils.rm_rf(tmp) if tmp
|
|
139
|
+
end
|
|
65
140
|
end
|
data/spec/lib/pwn/memory_spec.rb
CHANGED
|
@@ -18,7 +18,7 @@ describe PWN::Memory do
|
|
|
18
18
|
it 'should support remember/recall/forget/clear' do
|
|
19
19
|
tmp = Dir.mktmpdir
|
|
20
20
|
stub_const('PWN::Memory::MEMORY_FILE', File.join(tmp, 'memory.json'))
|
|
21
|
-
PWN::Memory.clear
|
|
21
|
+
PWN::Memory.clear(force: true)
|
|
22
22
|
PWN::Memory.remember(key: :test_fact, value: 'pwn-ai test memory', category: :fact)
|
|
23
23
|
res = PWN::Memory.recall(query: 'test')
|
|
24
24
|
expect(res.keys).to include(:test_fact)
|
|
@@ -42,4 +42,49 @@ describe PWN::Memory do
|
|
|
42
42
|
ensure
|
|
43
43
|
FileUtils.rm_rf(tmp) if tmp
|
|
44
44
|
end
|
|
45
|
+
|
|
46
|
+
it 'enforces VALUE_MAX_CHARS on remember' do
|
|
47
|
+
tmp = Dir.mktmpdir
|
|
48
|
+
stub_const('PWN::Memory::MEMORY_FILE', File.join(tmp, 'memory.json'))
|
|
49
|
+
PWN::Memory.clear(force: true)
|
|
50
|
+
long = 'x' * (PWN::Memory::VALUE_MAX_CHARS + 50)
|
|
51
|
+
PWN::Memory.remember(key: :long_val, value: long, category: :fact)
|
|
52
|
+
v = PWN::Memory.load[:long_val][:value]
|
|
53
|
+
expect(v.bytesize).to be <= (PWN::Memory::VALUE_MAX_CHARS + 20)
|
|
54
|
+
expect(v).to include('[compacted]')
|
|
55
|
+
ensure
|
|
56
|
+
FileUtils.rm_rf(tmp) if tmp
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'refuses forget/clear on protected keys without force' do
|
|
60
|
+
tmp = Dir.mktmpdir
|
|
61
|
+
stub_const('PWN::Memory::MEMORY_FILE', File.join(tmp, 'memory.json'))
|
|
62
|
+
PWN::Memory.clear(force: true)
|
|
63
|
+
PWN::Memory.remember(key: :operator_pref_test, value: 'keep', category: :preference)
|
|
64
|
+
PWN::Memory.remember(key: :process_sop_test, value: 'keep', category: :lesson)
|
|
65
|
+
expect { PWN::Memory.forget(key: :operator_pref_test) }.to raise_error(/protected/)
|
|
66
|
+
expect { PWN::Memory.forget(key: :process_sop_test) }.to raise_error(/protected/)
|
|
67
|
+
expect { PWN::Memory.clear }.to raise_error(/force:true/)
|
|
68
|
+
expect(PWN::Memory.forget(key: :operator_pref_test, force: true)).to be true
|
|
69
|
+
expect(PWN::Memory.load.keys).not_to include(:operator_pref_test)
|
|
70
|
+
ensure
|
|
71
|
+
FileUtils.rm_rf(tmp) if tmp
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'lean! drops expired session_* but keeps protected prefixes' do
|
|
75
|
+
tmp = Dir.mktmpdir
|
|
76
|
+
stub_const('PWN::Memory::MEMORY_FILE', File.join(tmp, 'memory.json'))
|
|
77
|
+
PWN::Memory.clear(force: true)
|
|
78
|
+
PWN::Memory.remember(key: :operator_pref_x, value: 'pref', category: :preference)
|
|
79
|
+
PWN::Memory.remember(key: :session_old, value: 'ephemeral', category: :fact)
|
|
80
|
+
mem = PWN::Memory.load
|
|
81
|
+
mem[:session_old][:timestamp] = (Time.now.utc - (PWN::Memory::EPHEMERAL_TTL_SECS + 100)).iso8601
|
|
82
|
+
PWN::Memory.save(mem: mem)
|
|
83
|
+
res = PWN::Memory.lean!
|
|
84
|
+
expect(res[:removed]).to be >= 1
|
|
85
|
+
expect(PWN::Memory.load.keys).to include(:operator_pref_x)
|
|
86
|
+
expect(PWN::Memory.load.keys).not_to include(:session_old)
|
|
87
|
+
ensure
|
|
88
|
+
FileUtils.rm_rf(tmp) if tmp
|
|
89
|
+
end
|
|
45
90
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'spec_helper'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
require 'fileutils'
|
|
4
6
|
|
|
5
7
|
describe PWN::Sessions do
|
|
6
8
|
it 'should display information for authors' do
|
|
@@ -19,7 +21,44 @@ describe PWN::Sessions do
|
|
|
19
21
|
PWN::Sessions.append(session_id: s[:id], role: 'user', content: 'test input')
|
|
20
22
|
t = PWN::Sessions.load(session_id: s[:id])
|
|
21
23
|
expect(t.size).to be >= 2
|
|
22
|
-
|
|
24
|
+
# Hot sessions are pinned — force required for intentional delete.
|
|
25
|
+
expect { PWN::Sessions.delete(session_id: s[:id]) }.to raise_error(/protected/)
|
|
26
|
+
PWN::Sessions.delete(session_id: s[:id], force: true)
|
|
23
27
|
expect(PWN::Sessions.list.any? { |x| x[:id] == s[:id] }).to be false
|
|
24
28
|
end
|
|
29
|
+
|
|
30
|
+
it 'caps tool/assistant content on append per TOOL_CONTENT_MAX/ASSISTANT_CONTENT_MAX' do
|
|
31
|
+
s = PWN::Sessions.create(title: 'cap test')
|
|
32
|
+
tool_big = 'T' * (PWN::Sessions::TOOL_CONTENT_MAX + 100)
|
|
33
|
+
asst_big = 'A' * (PWN::Sessions::ASSISTANT_CONTENT_MAX + 100)
|
|
34
|
+
PWN::Sessions.append(session_id: s[:id], role: 'tool', content: tool_big)
|
|
35
|
+
PWN::Sessions.append(session_id: s[:id], role: 'assistant', content: asst_big)
|
|
36
|
+
rows = PWN::Sessions.load(session_id: s[:id])
|
|
37
|
+
tool = rows.find { |e| e[:role] == 'tool' }
|
|
38
|
+
asst = rows.find { |e| e[:role] == 'assistant' }
|
|
39
|
+
expect(tool[:content].bytesize).to be <= (PWN::Sessions::TOOL_CONTENT_MAX + 20)
|
|
40
|
+
expect(asst[:content].bytesize).to be <= (PWN::Sessions::ASSISTANT_CONTENT_MAX + 20)
|
|
41
|
+
expect(tool[:content]).to include('[compacted]')
|
|
42
|
+
ensure
|
|
43
|
+
PWN::Sessions.delete(session_id: s[:id], force: true) if s
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'lean! respects MIN_STUB_BYTES and HOT_DAYS pins' do
|
|
47
|
+
tmp = Dir.mktmpdir
|
|
48
|
+
stub_const('PWN::Sessions::SESSIONS_DIR', tmp)
|
|
49
|
+
hot = PWN::Sessions.create(title: 'hot')
|
|
50
|
+
PWN::Sessions.append(session_id: hot[:id], role: 'user', content: 'substantive user line here')
|
|
51
|
+
# cold stub: tiny file outside hot window
|
|
52
|
+
stub_id = '19990101_000000_deadbeef'
|
|
53
|
+
stub_path = File.join(tmp, "#{stub_id}.jsonl")
|
|
54
|
+
File.write(stub_path, %({"role":"system","content":"Session started","timestamp":"1999-01-01T00:00:00Z"}
|
|
55
|
+
))
|
|
56
|
+
File.utime(Time.at(0), Time.at(0), stub_path)
|
|
57
|
+
res = PWN::Sessions.lean!(current_session_id: hot[:id])
|
|
58
|
+
expect(File.exist?(File.join(tmp, "#{hot[:id]}.jsonl"))).to be true
|
|
59
|
+
expect(File.exist?(stub_path)).to be false
|
|
60
|
+
expect(res[:deleted]).to be >= 1
|
|
61
|
+
ensure
|
|
62
|
+
FileUtils.rm_rf(tmp) if tmp
|
|
63
|
+
end
|
|
25
64
|
end
|
data/third_party/pwn_rdoc.jsonl
CHANGED
|
@@ -256,39 +256,50 @@
|
|
|
256
256
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::HackerOne.analyze Usage"},{"role":"assistant","content":"`PWN::AI::Agent::HackerOne.analyze`: Supported Method Parameters\n\nai_analysis = PWN::AI::Agent::HackerOne.analyze(\n\nrequest: 'required - dataset to analyze, such as bounty program details, scope details, or hacktivity details'\ntype: 'required - type of analysis to perform, such as :bounty_programs, :scope_details, or :hacktivity'\n\n)\n"}]}
|
|
257
257
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::HackerOne.authors Usage"},{"role":"assistant","content":"`PWN::AI::Agent::HackerOne.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
|
|
258
258
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::HackerOne.help Usage"},{"role":"assistant","content":"`PWN::AI::Agent::HackerOne.help`: "}]}
|
|
259
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.atomic_jsonl_write Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.atomic_jsonl_write`: "}]}
|
|
259
260
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.authors Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
|
|
260
261
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.auto_introspect Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.auto_introspect`: Supported Method Parameters\n\nPWN::AI::Agent::Learning.auto_introspect(\n\nsession_id: 'required - id of the just-completed session',\nrequest: 'optional - original user request (for outcome logging)',\nfinal: 'optional - final assistant answer (for outcome logging)'\n\n)\n\nCalled by Loop.run when PWN::Env[:agent] is truthy. Never raises — learning must not break the primary loop.\n"}]}
|
|
261
262
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.auto_introspect_enabled? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.auto_introspect_enabled?`: "}]}
|
|
262
263
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.build_skill_from_session Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.build_skill_from_session`: "}]}
|
|
264
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.cause_crumb Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.cause_crumb`: "}]}
|
|
263
265
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.checkable_claim? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.checkable_claim?`: "}]}
|
|
264
266
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.compress_exemplar Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.compress_exemplar`: "}]}
|
|
265
267
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.compress_finetune_trace Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.compress_finetune_trace`: "}]}
|
|
266
268
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.consolidate Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.consolidate`: Supported Method Parameters\n\nremoved = PWN::AI::Agent::Learning.consolidate(\n\nmax_entries: 'optional - hard cap on PWN::Memory size (default MAX_MEMORY_ENTRIES)'\n\n)\n\nDeduplicates near-identical lesson values and prunes the oldest entries once the cap is exceeded so the injected MEMORY block stays high-signal.\n"}]}
|
|
269
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.display_task Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.display_task`: "}]}
|
|
267
270
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.distill_skill Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.distill_skill`: Supported Method Parameters\n\nskill = PWN::AI::Agent::Learning.distill_skill(\n\nname: 'required - snake_case name for the new skill',\nsession_id: 'optional - PWN::Sessions id to mine (uses its transcript)',\ncontent: 'optional - explicit markdown body; overrides transcript mining',\nreferences: 'optional - Array of reference URLs / CWE / CVE / ATT&CK ids'\n\n)\n"}]}
|
|
268
271
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.exemplars_for Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.exemplars_for`: Supported Method Parameters\n\nmsgs = PWN::AI::Agent::Learning.exemplars_for(\n\nrequest: 'required - current user request',\nlimit: 'optional - max exemplar traces to return (default 1)',\nmax_msgs: 'optional - cap on messages per exemplar (default 6)'\n\n)\n\nRetrieval-augmented BEHAVIOUR: keyword-matches request against prior successful outcomes in learning.jsonl, loads the matching session, and compresses its (user, tool, assistant) trace into a short few-shot exemplar Loop.run splices between system and user. Local models are dramatically better with 1 concrete example than with 25 abstract lessons.\n"}]}
|
|
269
272
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.export_finetune Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.export_finetune`: "}]}
|
|
270
273
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.fact_check_local_final Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.fact_check_local_final`: "}]}
|
|
271
274
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.flip_last_outcome Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.flip_last_outcome`: "}]}
|
|
272
275
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.fold_judge_into_metrics Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.fold_judge_into_metrics`: "}]}
|
|
276
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.gc_stores! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.gc_stores!`: Supported Method Parameters\n\nresult = PWN::AI::Agent::Learning.gc_stores!(\n\ndry_run: 'optional - Boolean (default false)',\ncurrent_session_id: 'optional - never delete this sessions id',\nmax_entries: 'optional - Memory consolidate cap',\nmax_rows: 'optional - learning.jsonl cap',\nretain_days: 'optional - outcome / session age floor'\n\n)\n"}]}
|
|
273
277
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.help Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.help`: "}]}
|
|
274
278
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.heuristic_lessons Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.heuristic_lessons`: "}]}
|
|
275
279
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.infer_success Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.infer_success`: "}]}
|
|
276
280
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.introspective_lessons Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.introspective_lessons`: "}]}
|
|
281
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.lean! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.lean!`: "}]}
|
|
282
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.maybe_prune_outcomes! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.maybe_prune_outcomes!`: "}]}
|
|
277
283
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.merge_cluster Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.merge_cluster`: "}]}
|
|
278
284
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.note_outcome Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.note_outcome`: Supported Method Parameters\n\nentry = PWN::AI::Agent::Learning.note_outcome(\n\ntask: 'required - short description of what was attempted',\nsuccess: 'required - Boolean, did the attempt achieve its goal',\ndetails: 'optional - free-form notes / error / evidence',\nsession_id: 'optional - PWN::Sessions id this outcome belongs to',\ntags: 'optional - Array of String labels for later retrieval'\n\n)\n"}]}
|
|
279
285
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.outcomes Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.outcomes`: Supported Method Parameters\n\nrows = PWN::AI::Agent::Learning.outcomes(\n\nlimit: 'optional - max entries returned newest-first (default 50)',\nsuccess: 'optional - filter by Boolean outcome',\ntag: 'optional - filter by tag substring'\n\n)\n"}]}
|
|
286
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.prefer_primary_tasks Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.prefer_primary_tasks`: "}]}
|
|
280
287
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.process_sop_text? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.process_sop_text?`: "}]}
|
|
281
288
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.promote_process_lesson Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.promote_process_lesson`: "}]}
|
|
289
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.prune_outcomes! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.prune_outcomes!`: Supported Method Parameters\n\nresult = PWN::AI::Agent::Learning.prune_outcomes!(\n\ndry_run: 'optional - Boolean (default false)',\nmax_rows: 'optional - hard cap (default MAX_OUTCOME_ROWS)',\nretain_days: 'optional - age floor for low-value drop',\nrecent_days: 'optional - always keep newer than this'\n\n)\n\nKeep gold RL rows (success+score>=0.6+session_id), recent window, high-value tags, and near-miss failures. Dedupe by task+success keeping best score. Truncate details. Never sacrifices exemplar pool.\n"}]}
|
|
282
290
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.purge_noise Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.purge_noise`: Supported Method Parameters\n\nPWN::AI::Agent::Learning.purge_noise\n\nOne-shot GC of the pre-R1 garbage: drops every PWN::Memory entry matching the old ‘SUCCESS: <req> — <final>` / `Avoid repeating failure pattern from <tool>: {“success”:true` shapes. Run once after upgrading; subsequent writes never produce these.\n"}]}
|
|
291
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.reconcile_verdict_tags! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.reconcile_verdict_tags!`: "}]}
|
|
283
292
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.recover_predicted_from_session Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.recover_predicted_from_session`: "}]}
|
|
284
293
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.reflect Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.reflect`: Supported Method Parameters\n\nreport = PWN::AI::Agent::Learning.reflect(\n\nsession_id: 'required - PWN::Sessions id to analyse',\ndry_run: 'optional - when true, do not write to Memory/Skills (default false)'\n\n)\n\nUses PWN::AI::Agent::Reflect (when available) to LLM-summarise the session into structured lessons. Falls back to a heuristic extractor when module_reflection is disabled so learning never stops.\n"}]}
|
|
285
294
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.reset Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.reset`: Supported Method Parameters\n\nPWN::AI::Agent::Learning.reset\n"}]}
|
|
286
295
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.semantic_merge Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.semantic_merge`: "}]}
|
|
287
296
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.sharegpt_role Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.sharegpt_role`: "}]}
|
|
297
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.should_gc_stores? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.should_gc_stores?`: "}]}
|
|
288
298
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.skills_dir Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.skills_dir`: "}]}
|
|
289
299
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.stats Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.stats`: Supported Method Parameters\n\nstats = PWN::AI::Agent::Learning.stats\n"}]}
|
|
290
300
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.to_context Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.to_context`: Supported Method Parameters\n\nctx = PWN::AI::Agent::Learning.to_context(\n\nlimit: 'optional - number of recent outcomes to surface (default 5)'\n\n)\n"}]}
|
|
291
301
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.transcript_text Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.transcript_text`: "}]}
|
|
302
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.verdict_for_score Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.verdict_for_score`: "}]}
|
|
292
303
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.active_engine Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.active_engine`: "}]}
|
|
293
304
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.agent_flag Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.agent_flag`: "}]}
|
|
294
305
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.append_session Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.append_session`: "}]}
|
|
@@ -357,6 +368,7 @@
|
|
|
357
368
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Mistakes.find Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Mistakes.find`: Supported Method Parameters\n\nentry = PWN::AI::Agent::Mistakes.find(\n\nsignature: 'optional - exact signature to fetch',\ntool: 'optional - with error:, compute signature and fetch',\nerror: 'optional - raw error text (used with tool:)'\n\n)\n"}]}
|
|
358
369
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Mistakes.for_tool Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Mistakes.for_tool`: Supported Method Parameters\n\nrows = PWN::AI::Agent::Mistakes.for_tool(\n\ntool: 'required - tool name',\nunresolved_only: 'optional - default false'\n\n)\n"}]}
|
|
359
370
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Mistakes.help Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Mistakes.help`: "}]}
|
|
371
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Mistakes.lean! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Mistakes.lean!`: Supported Method Parameters\n\nresult = PWN::AI::Agent::Mistakes.lean!(\n\ndry_run: 'optional - Boolean (default false)',\nmax_resolved_kept: 'optional - cap on resolved-with-fix records',\nresolved_min_age_days: 'optional - age before resolved count=1 may drop'\n\n)\n\nCompact text fields on every record. Never drops unresolved, regressed, or high-count repeaters. Aged resolved-once fixes may drop after Memory already holds mistake_fix_<sig>.\n"}]}
|
|
360
372
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Mistakes.load Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Mistakes.load`: Supported Method Parameters\n\nstore = PWN::AI::Agent::Mistakes.load\n"}]}
|
|
361
373
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Mistakes.normalize_error Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Mistakes.normalize_error`: "}]}
|
|
362
374
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Mistakes.park Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Mistakes.park`: "}]}
|
|
@@ -1220,11 +1232,13 @@
|
|
|
1220
1232
|
{"messages":[{"role":"user","content":"PWN::FFI::Volk.sqrt Usage"},{"role":"assistant","content":"`PWN::FFI::Volk.sqrt`: Supported Method Parameters\n\nout = PWN::FFI::Volk.sqrt(samples: Array<Float>)\n"}]}
|
|
1221
1233
|
{"messages":[{"role":"user","content":"PWN::FFI::Volk.unpack_s16le Usage"},{"role":"assistant","content":"`PWN::FFI::Volk.unpack_s16le`: Supported Method Parameters\n\nsamples = PWN::FFI::Volk.unpack_s16le(\n\ndata: 'required - raw String of little-endian signed 16-bit PCM'\n\n) Returns Array<Float> normalised to -1.0..1.0 (same contract as PWN::SDR::Decoder::DSP.unpack_s16le). VOLK treats ‘scalar` as a divisor, so pass 32768.0 to get unit-range floats.\n"}]}
|
|
1222
1234
|
{"messages":[{"role":"user","content":"PWN::Memory.authors Usage"},{"role":"assistant","content":"`PWN::Memory.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
|
|
1223
|
-
{"messages":[{"role":"user","content":"PWN::Memory.clear Usage"},{"role":"assistant","content":"`PWN::Memory.clear`: Supported Method Parameters\n\nPWN::Memory.clear\n"}]}
|
|
1224
|
-
{"messages":[{"role":"user","content":"PWN::Memory.forget Usage"},{"role":"assistant","content":"`PWN::Memory.forget`: Supported Method Parameters\n\nPWN::Memory.forget(
|
|
1235
|
+
{"messages":[{"role":"user","content":"PWN::Memory.clear Usage"},{"role":"assistant","content":"`PWN::Memory.clear`: Supported Method Parameters\n\nPWN::Memory.clear(force: true)\n"}]}
|
|
1236
|
+
{"messages":[{"role":"user","content":"PWN::Memory.forget Usage"},{"role":"assistant","content":"`PWN::Memory.forget`: Supported Method Parameters\n\nPWN::Memory.forget(\n\nkey: :some_key,\nforce: 'optional - Boolean bypass protect policy (default false)'\n\n)\n"}]}
|
|
1225
1237
|
{"messages":[{"role":"user","content":"PWN::Memory.help Usage"},{"role":"assistant","content":"`PWN::Memory.help`: "}]}
|
|
1238
|
+
{"messages":[{"role":"user","content":"PWN::Memory.lean! Usage"},{"role":"assistant","content":"`PWN::Memory.lean!`: Supported Method Parameters\n\nresult = PWN::Memory.lean!(\n\ndry_run: 'optional - Boolean plan only (default false)',\nvalue_max_chars: 'optional - truncate values (default VALUE_MAX_CHARS)',\nephemeral_ttl_secs: 'optional - drop expired session_* keys'\n\n)\n"}]}
|
|
1226
1239
|
{"messages":[{"role":"user","content":"PWN::Memory.load Usage"},{"role":"assistant","content":"`PWN::Memory.load`: Supported Method Parameters\n\nmemory = PWN::Memory.load\n"}]}
|
|
1227
1240
|
{"messages":[{"role":"user","content":"PWN::Memory.load_raw_or_empty Usage"},{"role":"assistant","content":"`PWN::Memory.load_raw_or_empty`: "}]}
|
|
1241
|
+
{"messages":[{"role":"user","content":"PWN::Memory.protected_entry? Usage"},{"role":"assistant","content":"`PWN::Memory.protected_entry?`: "}]}
|
|
1228
1242
|
{"messages":[{"role":"user","content":"PWN::Memory.recall Usage"},{"role":"assistant","content":"`PWN::Memory.recall`: Supported Method Parameters\n\nresults = PWN::Memory.recall(\n\nquery: 'optional - string to search keys/values/categories (simple match)',\ncategory: 'optional - filter by category',\nlimit: 'optional - max results (default 50)'\n\n)\n"}]}
|
|
1229
1243
|
{"messages":[{"role":"user","content":"PWN::Memory.recall_semantic Usage"},{"role":"assistant","content":"`PWN::Memory.recall_semantic`: Supported Method Parameters\n\nhits = PWN::Memory.recall_semantic(query: ‘nmap sweep’, limit: 6)\n"}]}
|
|
1230
1244
|
{"messages":[{"role":"user","content":"PWN::Memory.remember Usage"},{"role":"assistant","content":"`PWN::Memory.remember`: Supported Method Parameters\n\nPWN::Memory.remember(\n\nkey: 'required - Symbol or String key for the memory fact',\nvalue: 'required - The value (any JSON serializable)',\ncategory: 'optional - e.g. :fact, :preference, :lesson, :env (default: :fact)',\nsource: 'optional - :human | :reflect | :heuristic | :resolve | :consolidate (M3 provenance)',\nconfidence: 'optional - 0.0..1.0 how sure the writer was (M3)',\nimportance: 'optional - 0.0..1.0 retrieval/eviction weight (M2/M3)',\nttl: 'optional - seconds until stale (M3; consolidate evicts stale low-conf first)'\n\n)\n"}]}
|
|
@@ -2378,13 +2392,17 @@
|
|
|
2378
2392
|
{"messages":[{"role":"user","content":"PWN::SDR::SonMicroRFID.write_tag Usage"},{"role":"assistant","content":"`PWN::SDR::SonMicroRFID.write_tag`: Supported Method Parameters\n\nPWN::SDR::SonMicroRFID.write_tag(\n\nson_micro_rfid_obj: 'required - son_micro_rfid_obj returned from #connect method',\nrfid_data: 'required - RFID data to write (see #read_tag for structure)',\nauthn: 'optional - authentication flag (default: false)',\nkey: 'optional - key for authentication (default: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])'\n\n)\n"}]}
|
|
2379
2393
|
{"messages":[{"role":"user","content":"PWN::Sessions.append Usage"},{"role":"assistant","content":"`PWN::Sessions.append`: Supported Method Parameters\n\nPWN::Sessions.append(\n\nsession_id: 'required',\nrole: 'user|assistant|system|observation',\ncontent: 'the message or obs'\n\n)\n"}]}
|
|
2380
2394
|
{"messages":[{"role":"user","content":"PWN::Sessions.authors Usage"},{"role":"assistant","content":"`PWN::Sessions.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
|
|
2395
|
+
{"messages":[{"role":"user","content":"PWN::Sessions.compact_transcript! Usage"},{"role":"assistant","content":"`PWN::Sessions.compact_transcript!`: "}]}
|
|
2381
2396
|
{"messages":[{"role":"user","content":"PWN::Sessions.create Usage"},{"role":"assistant","content":"`PWN::Sessions.create`: Supported Method Parameters\n\nsession = PWN::Sessions.create(\n\ntitle: 'optional - human title',\nsource: 'optional - e.g. pwn-ai-repl'\n\n)\n"}]}
|
|
2382
|
-
{"messages":[{"role":"user","content":"PWN::Sessions.delete Usage"},{"role":"assistant","content":"`PWN::Sessions.delete`: Supported Method Parameters\n\nPWN::Sessions.delete(
|
|
2397
|
+
{"messages":[{"role":"user","content":"PWN::Sessions.delete Usage"},{"role":"assistant","content":"`PWN::Sessions.delete`: Supported Method Parameters\n\nPWN::Sessions.delete(\n\nsession_id: 'required',\nforce: 'optional - Boolean bypass pin policy (default false)'\n\n)\n"}]}
|
|
2383
2398
|
{"messages":[{"role":"user","content":"PWN::Sessions.help Usage"},{"role":"assistant","content":"`PWN::Sessions.help`: "}]}
|
|
2399
|
+
{"messages":[{"role":"user","content":"PWN::Sessions.lean! Usage"},{"role":"assistant","content":"`PWN::Sessions.lean!`: Supported Method Parameters\n\nresult = PWN::Sessions.lean!(\n\ndry_run: 'optional - Boolean (default false)',\nretain_days: 'optional - drop unreferenced older than this',\nhot_days: 'optional - always keep newer than this',\nmax_files: 'optional - soft file cap after protections',\ncurrent_session_id: 'optional - never delete this id'\n\n)\n"}]}
|
|
2384
2400
|
{"messages":[{"role":"user","content":"PWN::Sessions.list Usage"},{"role":"assistant","content":"`PWN::Sessions.list`: Supported Method Parameters\n\nsessions = PWN::Sessions.list\n"}]}
|
|
2385
2401
|
{"messages":[{"role":"user","content":"PWN::Sessions.load Usage"},{"role":"assistant","content":"`PWN::Sessions.load`: Supported Method Parameters\n\ntranscript = PWN::Sessions.load(session_id: ‘required’)\n"}]}
|
|
2402
|
+
{"messages":[{"role":"user","content":"PWN::Sessions.protected_session_ids Usage"},{"role":"assistant","content":"`PWN::Sessions.protected_session_ids`: "}]}
|
|
2386
2403
|
{"messages":[{"role":"user","content":"PWN::Sessions.sessions_dir Usage"},{"role":"assistant","content":"`PWN::Sessions.sessions_dir`: Supported Method Parameters\n\ndir = PWN::Sessions.sessions_dir\n"}]}
|
|
2387
2404
|
{"messages":[{"role":"user","content":"PWN::Sessions.stats Usage"},{"role":"assistant","content":"`PWN::Sessions.stats`: Supported Method Parameters\n\nstats = PWN::Sessions.stats\n"}]}
|
|
2405
|
+
{"messages":[{"role":"user","content":"PWN::Sessions.stub_session? Usage"},{"role":"assistant","content":"`PWN::Sessions.stub_session?`: "}]}
|
|
2388
2406
|
{"messages":[{"role":"user","content":"PWN::Sessions.to_response_history Usage"},{"role":"assistant","content":"`PWN::Sessions.to_response_history`: Supported Method Parameters\n\nhistory_for_ai = PWN::Sessions.to_response_history(session_id:) (converts transcript to the response_history format used by PWN::AI::* .chat)\n"}]}
|
|
2389
2407
|
{"messages":[{"role":"user","content":"PWN::Setup.ai_active_engine Usage"},{"role":"assistant","content":"`PWN::Setup.ai_active_engine`: "}]}
|
|
2390
2408
|
{"messages":[{"role":"user","content":"PWN::Setup.ai_key_configured? Usage"},{"role":"assistant","content":"`PWN::Setup.ai_key_configured?`: "}]}
|
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.
|
|
4
|
+
version: 0.5.665
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 0day Inc.
|
|
@@ -2309,6 +2309,7 @@ files:
|
|
|
2309
2309
|
- spec/lib/pwn/ai/agent/extrospection_spec.rb
|
|
2310
2310
|
- spec/lib/pwn/ai/agent/gqrx_spec.rb
|
|
2311
2311
|
- spec/lib/pwn/ai/agent/hacker_one_spec.rb
|
|
2312
|
+
- spec/lib/pwn/ai/agent/lean_stores_spec.rb
|
|
2312
2313
|
- spec/lib/pwn/ai/agent/learning_spec.rb
|
|
2313
2314
|
- spec/lib/pwn/ai/agent/loop_spec.rb
|
|
2314
2315
|
- spec/lib/pwn/ai/agent/metrics_spec.rb
|