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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d14d73b9f076c18a1248f868c7febb2666758cfcf522493b850d3bc3e0a09ed2
4
- data.tar.gz: ae83a33e35025aa612b82d48d0ccab99bea2e5a4b20d1d2ffff81189233739b1
3
+ metadata.gz: 7e88bcfaae1b1859df50e16528f8460a31cab0d7a8168206b9f48c3541c00b88
4
+ data.tar.gz: c143b22d04b09c1c30a7f6b2f7bfe17cf3dfb8812484db4e42daf9565ba12668
5
5
  SHA512:
6
- metadata.gz: 9d39cbfff72ee966e15990f6d2329edc0fc3671bdc7b5c7f11ebb29587d4e93f64a447f9e5bb6fde2dac84160e1f4764a9143001866c85b730681fd903541340
7
- data.tar.gz: d8f6e3f3e50605efde9a8db3fa96daf4a385d599b98a5fe787af62a1d7ed9c0232da987f7320cb9a07001208d0a21f96767530a943d819b14c43841fe0d867d8
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: v[:score].to_f >= 0.6,
298
- score: v[:score],
299
- details: "offline_judge #{v[:verdict]}(#{v[:score]}) #{v[:rationale]}",
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: %w[offline_judge auto]
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
- rows = outcomes(limit: limit)
164
- fails = outcomes(limit: 200, success: false).first(limit)
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
- " #{flag} #{r[:task].to_s[0, 100]} (#{r[:timestamp]})"
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
- ok = v[:score] >= 0.6
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: opts[:request].to_s[0, 120],
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PWN
4
- VERSION = '0.5.663'
4
+ VERSION = '0.5.664'
5
5
  end
@@ -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
@@ -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`: "}]}
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.663
4
+ version: 0.5.664
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.