pwn 0.5.663 → 0.5.664
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 +132 -7
- data/lib/pwn/version.rb +1 -1
- data/spec/lib/pwn/ai/agent/learning_spec.rb +61 -0
- data/third_party/pwn_rdoc.jsonl +5 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7e88bcfaae1b1859df50e16528f8460a31cab0d7a8168206b9f48c3541c00b88
|
|
4
|
+
data.tar.gz: c143b22d04b09c1c30a7f6b2f7bfe17cf3dfb8812484db4e42daf9565ba12668
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 06d533eb382b1b6509684e15db4dcb8e9bdd24d9ac628e1f07c090bfd7f099e7a608ef61e2c87969e54f245736fb6a10f170042cd650f1dbd023816c788e662c
|
|
7
|
+
data.tar.gz: c3243b7fd533a14ad0825546b60b88b465ae8bdc5b476f001dd00cad98788d73173b578c981467a6ec664f1e60815d4bd2f3efa6a6b659e17b85e6ab6d4ad09d
|
|
@@ -292,13 +292,21 @@ module PWN
|
|
|
292
292
|
end
|
|
293
293
|
end
|
|
294
294
|
if commit && defined?(Learning)
|
|
295
|
+
# P29 — keep verdict tag score-aligned (same as auto_introspect).
|
|
296
|
+
sc = v[:score].to_f
|
|
297
|
+
verd = if defined?(Learning) && Learning.respond_to?(:verdict_for_score, true)
|
|
298
|
+
Learning.send(:verdict_for_score, score: sc).to_s
|
|
299
|
+
elsif sc >= 0.6 then 'solved'
|
|
300
|
+
elsif sc >= 0.3 then 'partial'
|
|
301
|
+
else 'wrong'
|
|
302
|
+
end
|
|
295
303
|
Learning.note_outcome(
|
|
296
304
|
task: req[0, 120],
|
|
297
|
-
success:
|
|
298
|
-
score:
|
|
299
|
-
details: "offline_judge #{
|
|
305
|
+
success: sc >= 0.6,
|
|
306
|
+
score: sc,
|
|
307
|
+
details: "offline_judge #{verd}(#{sc.round(2)}) #{v[:rationale]}",
|
|
300
308
|
session_id: sid,
|
|
301
|
-
tags:
|
|
309
|
+
tags: ['offline_judge', 'auto', verd]
|
|
302
310
|
)
|
|
303
311
|
end
|
|
304
312
|
scored << { session_id: sid, score: v[:score], verdict: v[:verdict] }
|
|
@@ -160,13 +160,33 @@ module PWN
|
|
|
160
160
|
|
|
161
161
|
public_class_method def self.to_context(opts = {})
|
|
162
162
|
limit = opts[:limit] || 5
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
# Fetch a wider window so prefer_primary_tasks can drop critic/red_team
|
|
164
|
+
# envelope rows (REQUEST:/GOAL: prefixes) without starving the block.
|
|
165
|
+
rows = prefer_primary_tasks(rows: outcomes(limit: limit * 4)).first(limit)
|
|
166
|
+
fails = prefer_primary_tasks(rows: outcomes(limit: 200, success: false))
|
|
167
|
+
# Do not mirror the same ids under both headings — that doubled the
|
|
168
|
+
# failure signal and made RECENT OUTCOMES == RECENT FAILURES when the
|
|
169
|
+
# last N attempts all failed (the injected block looked "stuck").
|
|
170
|
+
row_ids = rows.map { |r| r[:id] }.compact
|
|
171
|
+
fails = fails.reject { |r| row_ids.include?(r[:id]) }.first(limit)
|
|
165
172
|
return '' if rows.empty? && fails.empty?
|
|
166
173
|
|
|
167
174
|
fmt = lambda do |r|
|
|
168
|
-
flag = r[:success]
|
|
169
|
-
|
|
175
|
+
flag = case r[:success]
|
|
176
|
+
when true then '✓'
|
|
177
|
+
when 'soft', :soft then '∼'
|
|
178
|
+
else '✗'
|
|
179
|
+
end
|
|
180
|
+
score = r.key?(:score) ? format('%.2f', r[:score].to_f) : '-'
|
|
181
|
+
task = display_task(task: r[:task])
|
|
182
|
+
line = " #{flag} [#{score}] #{task} (#{r[:timestamp]})"
|
|
183
|
+
# Surface a one-line cause crumb so the agent can actually learn
|
|
184
|
+
# from failures instead of only seeing that they failed.
|
|
185
|
+
if r[:success] != true
|
|
186
|
+
crumb = cause_crumb(details: r[:details])
|
|
187
|
+
line += "\n cause: #{crumb}" unless crumb.empty?
|
|
188
|
+
end
|
|
189
|
+
line
|
|
170
190
|
end
|
|
171
191
|
s = stats
|
|
172
192
|
jm = s[:judge_mean]
|
|
@@ -468,7 +488,12 @@ module PWN
|
|
|
468
488
|
v = Reward.judge(request: opts[:request], final: opts[:final], session_id: session_id, proxy_ok: proxy_ok) if defined?(Reward)
|
|
469
489
|
v ||= { score: proxy_ok ? 1.0 : 0.0, success: proxy_ok, verdict: proxy_ok ? :solved : :wrong }
|
|
470
490
|
v[:score] = [v[:score], 0.3].min if crit[:verdict] == :flaw
|
|
471
|
-
|
|
491
|
+
# P29 — critic floor used to leave stale verdict=:solved at score=0.3,
|
|
492
|
+
# producing learning.jsonl rows tagged "solved" with success=false
|
|
493
|
+
# (116+ rows). Always resync verdict/success from the final score.
|
|
494
|
+
v[:verdict] = verdict_for_score(score: v[:score])
|
|
495
|
+
v[:success] = v[:score].to_f >= 0.6
|
|
496
|
+
ok = v[:success]
|
|
472
497
|
|
|
473
498
|
# W1 pending user_correction pair
|
|
474
499
|
pend = Thread.current[:pwn_pending_pref]
|
|
@@ -515,11 +540,14 @@ module PWN
|
|
|
515
540
|
outcome_tags = ['auto', 'loop', v[:verdict].to_s]
|
|
516
541
|
outcome_tags << plan_cov[:tag] if plan_cov && plan_cov[:tag]
|
|
517
542
|
outcome_tags << "plan_cover=#{plan_cov[:score]}" if plan_cov && plan_cov[:total].to_i.positive?
|
|
543
|
+
# P29 — persist the bare user ask (strip REQUEST:/GOAL: envelopes at write time)
|
|
544
|
+
task_txt = display_task(task: opts[:request].to_s)
|
|
545
|
+
task_txt = opts[:request].to_s[0, 100] if task_txt.empty?
|
|
518
546
|
note_outcome(
|
|
519
|
-
task:
|
|
547
|
+
task: task_txt,
|
|
520
548
|
success: ok,
|
|
521
549
|
score: v[:score],
|
|
522
|
-
details: "#{v[:verdict]}(#{v[:score].round(2)}) #{v[:rationale]} | #{opts[:final].to_s[0, 200]}",
|
|
550
|
+
details: "#{v[:verdict]}(#{v[:score].to_f.round(2)}) #{v[:rationale]} | #{opts[:final].to_s[0, 200]}",
|
|
523
551
|
session_id: session_id,
|
|
524
552
|
tags: outcome_tags
|
|
525
553
|
)
|
|
@@ -794,6 +822,103 @@ module PWN
|
|
|
794
822
|
nil
|
|
795
823
|
end
|
|
796
824
|
|
|
825
|
+
# P29 — map score → verdict with the same thresholds as Reward.judge.
|
|
826
|
+
private_class_method def self.verdict_for_score(opts = {})
|
|
827
|
+
s = opts[:score].to_f
|
|
828
|
+
return :solved if s >= 0.6
|
|
829
|
+
return :partial if s >= 0.3
|
|
830
|
+
|
|
831
|
+
:wrong
|
|
832
|
+
end
|
|
833
|
+
|
|
834
|
+
# Strip critic/red_team envelope prefixes so the injected block shows
|
|
835
|
+
# the human ask, not "REQUEST:\n…\nANSWER:" / "GOAL:\n…\nPLAN:".
|
|
836
|
+
private_class_method def self.display_task(opts = {})
|
|
837
|
+
t = opts[:task].to_s.gsub(/\s+/, ' ').strip
|
|
838
|
+
if t.match?(/\AREQUEST:\s*/i)
|
|
839
|
+
body = t.sub(/\AREQUEST:\s*/i, '')
|
|
840
|
+
body = body.split(/\bANSWER:\s*/i, 2).first.to_s
|
|
841
|
+
t = body.strip
|
|
842
|
+
elsif t.match?(/\AGOAL:\s*/i)
|
|
843
|
+
body = t.sub(/\AGOAL:\s*/i, '')
|
|
844
|
+
body = body.split(/\bPLAN:\s*/i, 2).first.to_s
|
|
845
|
+
t = body.strip
|
|
846
|
+
end
|
|
847
|
+
t[0, 100]
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
# Prefer bare user goals over REQUEST:/GOAL: swarm envelopes when both
|
|
851
|
+
# describe the same underlying attempt (offline_judge + critic sessions).
|
|
852
|
+
private_class_method def self.prefer_primary_tasks(opts = {})
|
|
853
|
+
rows = Array(opts[:rows])
|
|
854
|
+
return rows if rows.empty?
|
|
855
|
+
|
|
856
|
+
scored = rows.map do |r|
|
|
857
|
+
t = r[:task].to_s
|
|
858
|
+
envelope = t.match?(/\A\s*(REQUEST:|GOAL:)/i) ? 1 : 0
|
|
859
|
+
# Higher is better: bare task first, then newer (rows already newest-first)
|
|
860
|
+
[r, -envelope]
|
|
861
|
+
end
|
|
862
|
+
# stable: keep relative order within same envelope rank
|
|
863
|
+
scored.sort_by.with_index { |(_, rank), i| [rank, i] }.map(&:first)
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
private_class_method def self.cause_crumb(opts = {})
|
|
867
|
+
d = opts[:details].to_s.gsub(/\s+/, ' ').strip
|
|
868
|
+
return '' if d.empty?
|
|
869
|
+
|
|
870
|
+
# Prefer explicit FLAW / CORRECTED crumbs; else verdict(score) head.
|
|
871
|
+
if (m = d.match(/\bFLAW:\s*(.+)\z/i)) || (m = d.match(/\bFLAW:\s*([^|]+)/i))
|
|
872
|
+
return m[1].to_s.strip[0, 120]
|
|
873
|
+
end
|
|
874
|
+
if (m = d.match(/\bCORRECTED:\s*(.+)\z/i))
|
|
875
|
+
return "corrected: #{m[1].to_s.strip[0, 100]}"
|
|
876
|
+
end
|
|
877
|
+
|
|
878
|
+
d[0, 120]
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
# One-shot / on-load repair: rewrite tags+details where verdict label
|
|
882
|
+
# disagrees with score (solved @ 0.3 etc.). Safe to call repeatedly.
|
|
883
|
+
public_class_method def self.reconcile_verdict_tags!(opts = {})
|
|
884
|
+
return { repaired: 0 } unless File.exist?(LEARNING_FILE)
|
|
885
|
+
|
|
886
|
+
dry = opts[:dry_run] ? true : false
|
|
887
|
+
repaired = 0
|
|
888
|
+
lines = File.readlines(LEARNING_FILE)
|
|
889
|
+
out = lines.map do |l|
|
|
890
|
+
r = JSON.parse(l, symbolize_names: true)
|
|
891
|
+
score = r.key?(:score) ? r[:score].to_f : nil
|
|
892
|
+
next l if score.nil?
|
|
893
|
+
|
|
894
|
+
want = verdict_for_score(score: score).to_s
|
|
895
|
+
tags = Array(r[:tags]).map(&:to_s)
|
|
896
|
+
stale = tags & %w[solved partial wrong unknown]
|
|
897
|
+
next l if stale.empty? || stale.include?(want)
|
|
898
|
+
|
|
899
|
+
repaired += 1
|
|
900
|
+
next l if dry
|
|
901
|
+
|
|
902
|
+
cleaned = tags - %w[solved partial wrong unknown]
|
|
903
|
+
cleaned << want
|
|
904
|
+
r[:tags] = cleaned
|
|
905
|
+
# Fix leading "solved(0.3)" style details head when present
|
|
906
|
+
det = r[:details].to_s
|
|
907
|
+
r[:details] = det.sub(
|
|
908
|
+
/\A(solved|partial|wrong|unknown)\(\d+(?:\.\d+)?\)/i,
|
|
909
|
+
"#{want}(#{format('%.2f', score)})"
|
|
910
|
+
)
|
|
911
|
+
r[:success] = (score >= 0.6) if [true, false].include?(r[:success])
|
|
912
|
+
"#{JSON.generate(r)}\n"
|
|
913
|
+
rescue StandardError
|
|
914
|
+
l
|
|
915
|
+
end
|
|
916
|
+
File.write(LEARNING_FILE, out.join) if !dry && repaired.positive?
|
|
917
|
+
{ repaired: repaired, dry_run: dry }
|
|
918
|
+
rescue StandardError => e
|
|
919
|
+
{ repaired: 0, error: "#{e.class}: #{e.message}" }
|
|
920
|
+
end
|
|
921
|
+
|
|
797
922
|
private_class_method def self.auto_introspect_enabled?
|
|
798
923
|
return false unless defined?(PWN::Env) && PWN::Env.is_a?(Hash)
|
|
799
924
|
|
data/lib/pwn/version.rb
CHANGED
|
@@ -86,4 +86,65 @@ describe PWN::AI::Agent::Learning do
|
|
|
86
86
|
ensure
|
|
87
87
|
FileUtils.rm_rf(tmp) if defined?(tmp) && tmp
|
|
88
88
|
end
|
|
89
|
+
|
|
90
|
+
it 'P29 resyncs verdict after critic floor and strips request envelopes in to_context' do
|
|
91
|
+
tmp = Dir.mktmpdir
|
|
92
|
+
stub_const('PWN::AI::Agent::Learning::LEARNING_FILE', File.join(tmp, 'learning.jsonl'))
|
|
93
|
+
stub_const('PWN::Memory::MEMORY_FILE', File.join(tmp, 'memory.json'))
|
|
94
|
+
|
|
95
|
+
PWN::AI::Agent::Learning.reset
|
|
96
|
+
|
|
97
|
+
# Critic-capped solved-at-0.3 pattern (pre-P29 poison)
|
|
98
|
+
PWN::AI::Agent::Learning.note_outcome(
|
|
99
|
+
task: "REQUEST:\nwhats the bottom line?\n\nANSWER:\nfake bottom line",
|
|
100
|
+
success: false,
|
|
101
|
+
score: 0.3,
|
|
102
|
+
details: 'solved(0.3) heuristic overlap=0.75 ratio=1.0 | fake bottom line',
|
|
103
|
+
tags: %w[auto loop solved plan_cover_high]
|
|
104
|
+
)
|
|
105
|
+
PWN::AI::Agent::Learning.note_outcome(
|
|
106
|
+
task: 'real bare goal that failed',
|
|
107
|
+
success: false,
|
|
108
|
+
score: 0.22,
|
|
109
|
+
details: 'wrong(0.22) heuristic overlap=0.06 | blew up',
|
|
110
|
+
tags: %w[auto loop wrong]
|
|
111
|
+
)
|
|
112
|
+
PWN::AI::Agent::Learning.note_outcome(
|
|
113
|
+
task: 'successful scan',
|
|
114
|
+
success: true,
|
|
115
|
+
score: 0.9,
|
|
116
|
+
details: 'solved(0.9) ok',
|
|
117
|
+
tags: %w[auto loop solved]
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# reconcile should flip solved→partial on the 0.3 row
|
|
121
|
+
rep = PWN::AI::Agent::Learning.reconcile_verdict_tags!
|
|
122
|
+
expect(rep[:repaired]).to be >= 1
|
|
123
|
+
|
|
124
|
+
rows = PWN::AI::Agent::Learning.outcomes(limit: 20)
|
|
125
|
+
fixed = rows.find { |r| (r[:score].to_f - 0.3).abs < 0.001 }
|
|
126
|
+
expect(Array(fixed[:tags])).to include('partial')
|
|
127
|
+
expect(Array(fixed[:tags])).not_to include('solved')
|
|
128
|
+
expect(fixed[:details]).to match(/\Apartial\(0\.30?\)/)
|
|
129
|
+
|
|
130
|
+
ctx = PWN::AI::Agent::Learning.to_context(limit: 5)
|
|
131
|
+
# envelope stripped
|
|
132
|
+
expect(ctx).to include('whats the bottom line?')
|
|
133
|
+
expect(ctx).not_to match(/REQUEST:\nwhats/)
|
|
134
|
+
# score visible
|
|
135
|
+
expect(ctx).to match(/\[0\.30\]/)
|
|
136
|
+
# RECENT OUTCOMES and RECENT FAILURES must not be identical dumps
|
|
137
|
+
# (dedupe by id) — with 1 success + 2 fails, outcomes shows mix
|
|
138
|
+
expect(ctx).to include('successful scan')
|
|
139
|
+
expect(ctx).to include('cause:')
|
|
140
|
+
ensure
|
|
141
|
+
FileUtils.rm_rf(tmp) if defined?(tmp) && tmp
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it 'P29 verdict_for_score thresholds match Reward.judge' do
|
|
145
|
+
expect(PWN::AI::Agent::Learning.send(:verdict_for_score, score: 0.6)).to eq(:solved)
|
|
146
|
+
expect(PWN::AI::Agent::Learning.send(:verdict_for_score, score: 0.59)).to eq(:partial)
|
|
147
|
+
expect(PWN::AI::Agent::Learning.send(:verdict_for_score, score: 0.3)).to eq(:partial)
|
|
148
|
+
expect(PWN::AI::Agent::Learning.send(:verdict_for_score, score: 0.29)).to eq(:wrong)
|
|
149
|
+
end
|
|
89
150
|
end
|
data/third_party/pwn_rdoc.jsonl
CHANGED
|
@@ -260,10 +260,12 @@
|
|
|
260
260
|
{"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
261
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.auto_introspect_enabled? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.auto_introspect_enabled?`: "}]}
|
|
262
262
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.build_skill_from_session Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.build_skill_from_session`: "}]}
|
|
263
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.cause_crumb Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.cause_crumb`: "}]}
|
|
263
264
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.checkable_claim? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.checkable_claim?`: "}]}
|
|
264
265
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.compress_exemplar Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.compress_exemplar`: "}]}
|
|
265
266
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.compress_finetune_trace Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.compress_finetune_trace`: "}]}
|
|
266
267
|
{"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"}]}
|
|
268
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.display_task Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.display_task`: "}]}
|
|
267
269
|
{"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
270
|
{"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
271
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.export_finetune Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.export_finetune`: "}]}
|
|
@@ -277,9 +279,11 @@
|
|
|
277
279
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.merge_cluster Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.merge_cluster`: "}]}
|
|
278
280
|
{"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
281
|
{"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"}]}
|
|
282
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.prefer_primary_tasks Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.prefer_primary_tasks`: "}]}
|
|
280
283
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.process_sop_text? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.process_sop_text?`: "}]}
|
|
281
284
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.promote_process_lesson Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.promote_process_lesson`: "}]}
|
|
282
285
|
{"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"}]}
|
|
286
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.reconcile_verdict_tags! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.reconcile_verdict_tags!`: "}]}
|
|
283
287
|
{"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
288
|
{"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
289
|
{"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"}]}
|
|
@@ -289,6 +293,7 @@
|
|
|
289
293
|
{"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
294
|
{"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
295
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.transcript_text Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.transcript_text`: "}]}
|
|
296
|
+
{"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.verdict_for_score Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.verdict_for_score`: "}]}
|
|
292
297
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.active_engine Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.active_engine`: "}]}
|
|
293
298
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.agent_flag Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.agent_flag`: "}]}
|
|
294
299
|
{"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.append_session Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.append_session`: "}]}
|