pwn 0.5.647 → 0.5.650

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.
@@ -44,6 +44,7 @@ RSpec.describe 'PWN::AI::Agent reinforced feedback loop', :aggregate_failures do
44
44
  stub_const('PWN::AI::Agent::Reward::DPO_DIR', File.join(@tmp, 'finetune'))
45
45
  stub_const('PWN::AI::Agent::Curriculum::CURRICULUM_DIR', File.join(@tmp, 'curriculum'))
46
46
  stub_const('PWN::AI::Agent::Curriculum::MODELS_FILE', File.join(@tmp, 'curriculum', 'models.json'))
47
+ stub_const('PWN::AI::Agent::Curriculum::KPI_FILE', File.join(@tmp, 'curriculum_kpi.jsonl'))
47
48
  stub_const('PWN::Sessions::SESSIONS_DIR', File.join(@tmp, 'sessions'))
48
49
  stub_const('PWN::Memory::MEMORY_FILE', File.join(@tmp, 'memory.json'))
49
50
  stub_const('PWN::MemoryIndex::INDEX_FILE', File.join(@tmp, 'memory.idx')) if defined?(PWN::MemoryIndex)
@@ -1201,5 +1202,303 @@ RSpec.describe 'PWN::AI::Agent reinforced feedback loop', :aggregate_failures do
1201
1202
  expect(r[:verdict]).to eq :pass
1202
1203
  end
1203
1204
  end
1205
+
1206
+ # ═══════════════════════════════════════════════════════════════════════
1207
+ # Design-priority STATUS (post P14–P25) — P0 / P1 / P2
1208
+ # documentation/Reinforcement-Learning.md § Design-priority STATUS
1209
+ # ═══════════════════════════════════════════════════════════════════════
1210
+
1211
+ describe 'P0 · W1 generator diversity (TARGET_SOURCE_MIX + generator_mix)' do
1212
+ it 'exposes TARGET_SOURCE_MIX soft targets that sum to 1.0' do
1213
+ mix = reward::TARGET_SOURCE_MIX
1214
+ expect(mix.keys).to include('mistakes_resolve', 'curriculum', 'counterfactual', 'critic', 'user_correction')
1215
+ expect(mix.values.sum).to be_within(0.001).of(1.0)
1216
+ expect(mix['mistakes_resolve']).to be <= 0.40
1217
+ end
1218
+
1219
+ it 'generator_mix flags underfilled sources and returns recommendation' do
1220
+ # seed a few usable traj pairs skewed toward curriculum/critic
1221
+ 6.times do |i|
1222
+ reward.record_preference(
1223
+ prompt: "p#{i}",
1224
+ rejected: "bad answer #{i} " + ('x' * 40),
1225
+ chosen: "STRATEGY: use shell\nWINNING_TRACE: shell(uname)\nFINAL: linux #{i}",
1226
+ source: i.even? ? :curriculum : :critic,
1227
+ shape: :winning_trace
1228
+ )
1229
+ end
1230
+ m = reward.generator_mix
1231
+ expect(m[:n]).to be >= 5
1232
+ expect(m[:shares]).to be_a(Hash)
1233
+ expect(m[:targets]).to eq(reward::TARGET_SOURCE_MIX)
1234
+ expect(m[:urgent]).to include('counterfactual') # 0 share << 0.20 target
1235
+ expect(m[:healthy]).to eq(false)
1236
+ expect(m[:recommendation]).to match(/boost:|need_/)
1237
+ end
1238
+
1239
+ it 'write_source_quota reports underfilled against TARGET_SOURCE_MIX' do
1240
+ 12.times do |i|
1241
+ reward.record_preference(
1242
+ prompt: "q#{i}",
1243
+ rejected: "nope #{'y' * 40}",
1244
+ chosen: "STRATEGY: a\nWINNING_TRACE: t\nFINAL: ok #{i}",
1245
+ source: :curriculum,
1246
+ shape: :winning_trace
1247
+ )
1248
+ end
1249
+ q = reward.write_source_quota(source: 'counterfactual')
1250
+ expect(q[:underfilled]).to eq(true)
1251
+ expect(q[:target]).to eq(0.20)
1252
+ end
1253
+
1254
+ it 'counterfactual and critic force-run when generator_mix marks them urgent' do
1255
+ allow(reward).to receive(:generator_mix).and_return(
1256
+ urgent: %w[counterfactual critic], suppress: [], healthy: false, n: 20,
1257
+ recommendation: 'boost:counterfactual,critic'
1258
+ )
1259
+ # env flags OFF — mix_need should still open the gate
1260
+ @agent_cfg[:counterfactual] = false
1261
+ @agent_cfg[:critic] = false
1262
+ allow(curriculum).to receive(:in_curriculum?).and_return(false)
1263
+ allow(curriculum).to receive(:reflect_available?).and_return(false)
1264
+
1265
+ # counterfactual: stubs the expensive branch; we only assert it does not
1266
+ # short-circuit on disabled when mix_need is true. If still nil for other
1267
+ # reasons, at least enabled? gate is not the cause.
1268
+ allow(curriculum).to receive(:enabled?).and_call_original
1269
+ # critic text_only always works once gate opens
1270
+ r = curriculum.critic(request: 'q', final: 'a solid factual answer', text_only: true)
1271
+ expect(r[:source].to_s).not_to eq('disabled')
1272
+ end
1273
+ end
1274
+
1275
+ describe 'P0 · Introspect budget (soft/hard ms + stages_skipped)' do
1276
+ it 'defines INTROSPECT_SOFT_MS < INTROSPECT_HARD_MS' do
1277
+ expect(learning::INTROSPECT_SOFT_MS).to be < learning::INTROSPECT_HARD_MS
1278
+ expect(learning::INTROSPECT_SOFT_MS).to be_between(500, 10_000)
1279
+ end
1280
+
1281
+ it 'auto_introspect returns stages_skipped and respects hard budget' do
1282
+ @agent_cfg[:auto_introspect] = true
1283
+ # Force over_hard by collapsing thresholds
1284
+ stub_const('PWN::AI::Agent::Learning::INTROSPECT_SOFT_MS', 0)
1285
+ stub_const('PWN::AI::Agent::Learning::INTROSPECT_HARD_MS', 0)
1286
+ allow(loop_mod).to receive(:budget_exhaustion_hot?).and_return(false)
1287
+
1288
+ sid = 'spec_budget_introspect'
1289
+ dir = File.join(@tmp, 'sessions')
1290
+ FileUtils.mkdir_p(dir)
1291
+ payload = [
1292
+ { role: 'user', content: 'uname -a', timestamp: Time.now.utc.iso8601 },
1293
+ { role: 'assistant', content: 'Linux kali', timestamp: Time.now.utc.iso8601 }
1294
+ ].map { |h| JSON.generate(h) }.join("\n")
1295
+ File.write(File.join(dir, "#{sid}.jsonl"), "#{payload}\n")
1296
+
1297
+ r = learning.auto_introspect(session_id: sid, request: 'uname -a', final: 'Linux kali')
1298
+ expect(r).to be_a(Hash)
1299
+ expect(r).to have_key(:stages_run)
1300
+ expect(r).to have_key(:stages_skipped)
1301
+ expect(r[:stages_skipped]).to include(:prm) # hard skip
1302
+ expect(r[:elapsed_ms]).to be_a(Numeric)
1303
+ end
1304
+ end
1305
+
1306
+ describe 'P1 · Local judge calibration (shrinkage + confidence)' do
1307
+ it 'heuristic judge returns confidence and shrinks extreme local scores' do
1308
+ v = reward.judge(
1309
+ request: 'what is the kernel',
1310
+ final: 'The Linux kernel is a monolithic unix-like kernel used by kali.',
1311
+ trace: [],
1312
+ proxy_ok: true,
1313
+ commit: false
1314
+ )
1315
+ expect(v[:source].to_s).to eq('heuristic')
1316
+ expect(v[:confidence]).to be <= 0.5
1317
+ expect(v).to have_key(:score_raw)
1318
+ # local + no-trace cap pulls highs down
1319
+ expect(v[:score]).to be <= 0.45 if v[:score_raw].to_f >= 0.6
1320
+ expect(v[:rationale].to_s).to match(/P1:local_no_trace_cap|heuristic/)
1321
+ end
1322
+
1323
+ it 'Metrics.effective_rate scales distrust by judge_confidence' do
1324
+ # establish proxy success
1325
+ 10.times { metrics.record(name: 'shell', success: true, duration: 0.01) }
1326
+ # high judge scores but low confidence (heuristic)
1327
+ 8.times { metrics.record_judge(name: 'shell', score: 0.2, confidence: 0.35) }
1328
+ allow(metrics).to receive(:proxy_trust).and_return(0.3) # distrust 0.7
1329
+ rate = metrics.effective_rate(name: 'shell')
1330
+ conf = metrics.judge_confidence(name: 'shell')
1331
+ expect(conf).to be_within(0.05).of(0.35)
1332
+ # With conf=0.35, eff_d = 0.7*0.35 = 0.245 — still blended, not pure judge
1333
+ # pure judge would be ~0.2; pure proxy ~1.0; blend must sit between
1334
+ expect(rate).to be > 0.2
1335
+ expect(rate).to be < 1.0
1336
+ end
1337
+ end
1338
+
1339
+ describe 'P1 · Practice outer KPI (practice_kpi + repeating_trend)' do
1340
+ it 'practice_kpi snapshots repeating counts and writes KPI_FILE' do
1341
+ mistakes.record(tool: 'agent_loop', error: 'iteration budget exhausted without a final answer')
1342
+ 3.times { mistakes.record(tool: 'agent_loop', error: 'iteration budget exhausted without a final answer') }
1343
+ r = curriculum.practice_kpi(results: [{ resolved: true, mean_score: 0.8 }])
1344
+ expect(r[:repeating_n]).to be >= 1
1345
+ expect(r[:budget_repeating_n]).to be >= 1
1346
+ expect(r[:practiced]).to eq 1 # len(results) recorded tonight
1347
+ expect(r[:resolved_tonight]).to eq 1
1348
+ expect(File.exist?(curriculum::KPI_FILE)).to eq(true)
1349
+ expect(r[:trend]).to be_a(Hash)
1350
+ end
1351
+
1352
+ it 'repeating_trend reports baseline then delta across snapshots' do
1353
+ curriculum.practice_kpi(results: [])
1354
+ # bump repeating by recording more
1355
+ 4.times { mistakes.record(tool: 'assistant_answer', error: 'critic: [pwn-ai] iteration budget exhausted') }
1356
+ curriculum.practice_kpi(results: [])
1357
+ t = curriculum.repeating_trend
1358
+ expect(t[:samples]).to be >= 2
1359
+ expect(t).to have_key(:delta_repeating)
1360
+ expect(%i[improving flat regressing baseline]).to include(t[:status])
1361
+ end
1362
+ end
1363
+
1364
+ describe 'P2 · PRM sample efficiency (PRM_MIN_N / fleet coverage)' do
1365
+ it 'prm_advantage is 0 until n >= PRM_MIN_N then applies shrinkage' do
1366
+ expect(metrics::PRM_MIN_N).to eq 5
1367
+ expect(metrics::PRM_FULL_N).to eq 20
1368
+ 4.times { metrics.record_step_reward(name: 'shell', reward: 1.0) }
1369
+ expect(metrics.prm_n(name: 'shell')).to eq 4
1370
+ expect(metrics.prm_advantage(name: 'shell')).to eq 0.0
1371
+ metrics.record_step_reward(name: 'shell', reward: 1.0)
1372
+ expect(metrics.prm_n(name: 'shell')).to eq 5
1373
+ # single-tool zero-variance → damp*shrink keeps |adv| small but defined
1374
+ adv = metrics.prm_advantage(name: 'shell')
1375
+ expect(adv).to be_a(Numeric)
1376
+ end
1377
+
1378
+ it 'Registry.rank drops PRM delta until ≥3 tools are PRM-ready' do
1379
+ # only 1 tool ready → delta 0; rank still returns by keyword
1380
+ 10.times { metrics.record_step_reward(name: 'shell', reward: 1.0) }
1381
+ expect(metrics.prm_n(name: 'shell')).to be >= metrics::PRM_MIN_N
1382
+ ranked = registry.rank(query: 'run a shell command on the host')
1383
+ expect(ranked.map(&:name)).to include('shell')
1384
+ # spin up 3 ready tools — rank path must not raise
1385
+ %w[shell memory_recall sessions_list].each do |n|
1386
+ 10.times { metrics.record_step_reward(name: n, reward: 0.5) }
1387
+ end
1388
+ expect do
1389
+ registry.rank(query: 'run a shell command on the host')
1390
+ end.not_to raise_error
1391
+ end
1392
+ end
1393
+
1394
+ describe 'P2 · STATUS table is the flag authority (no archaeology)' do
1395
+ it 'Reinforcement-Learning.md ships a Design-priority STATUS section' do
1396
+ doc_path = File.expand_path('../../documentation/Reinforcement-Learning.md', __dir__)
1397
+ expect(File.exist?(doc_path)).to eq(true), "missing #{doc_path}"
1398
+ doc = File.read(doc_path)
1399
+ expect(doc).to match(/Design-priority STATUS/)
1400
+ expect(doc).to match(/generator_mix/)
1401
+ expect(doc).to match(/INTROSPECT_SOFT_MS/)
1402
+ expect(doc).to match(/practice_kpi/)
1403
+ expect(doc).to match(/PRM_MIN_N/)
1404
+ end
1405
+ end
1406
+
1407
+ # Design-priority ops closure — make STATUS success criteria reachable on-host
1408
+ describe 'P0 ops · nightly diet close + shape backfill + mix in prompt' do
1409
+ it 'infer_shape tags legacy winning_trace-like chosen sides' do
1410
+ row = {
1411
+ source: 'mistakes_resolve',
1412
+ chosen: "shell → {\"success\":true}\npwn_eval → ok\n#{'x' * 80}",
1413
+ rejected: "failed earlier #{'y' * 80}",
1414
+ shape: ''
1415
+ }
1416
+ s = reward.infer_shape(row: row)
1417
+ expect(s).to eq('winning_trace')
1418
+ end
1419
+
1420
+ it 'infer_shape tags long critic revisions as revised_answer' do
1421
+ row = {
1422
+ source: 'critic',
1423
+ chosen: 'A' * 250,
1424
+ rejected: 'B' * 100,
1425
+ shape: nil
1426
+ }
1427
+ expect(reward.infer_shape(row: row)).to eq('revised_answer')
1428
+ end
1429
+
1430
+ it 'scrub_preferences backfills shape on kept rows when dry_run:false path runs over tmp' do
1431
+ # exercise infer_shape + usable path without rewriting the live ledger
1432
+ raw = {
1433
+ source: 'curriculum',
1434
+ prompt: 'finish under 5 tools',
1435
+ rejected: "iteration budget exhausted #{'z' * 80}",
1436
+ chosen: "shell → ok\nmemory_recall → hit\n#{'w' * 100}",
1437
+ shape: ''
1438
+ }
1439
+ expect(reward.usable_preference?(row: raw.merge(shape: 'winning_trace'))).to eq(true)
1440
+ inferred = reward.infer_shape(row: raw)
1441
+ expect(reward::TRAJECTORY_SHAPES).to include(inferred)
1442
+ end
1443
+
1444
+ it 'offline_judge return hash reserves scrub/generator_mix/practice_kpi keys' do
1445
+ # dry structural contract: method source must call scrub + mix + kpi
1446
+ src = begin
1447
+ File.read(curriculum.instance_method(:offline_judge).source_location.first)
1448
+ rescue StandardError
1449
+ File.read('lib/pwn/ai/agent/curriculum.rb')
1450
+ end
1451
+ expect(src).to match(/scrub_preferences/)
1452
+ expect(src).to match(/generator_mix/)
1453
+ expect(src).to match(/practice_kpi/)
1454
+ end
1455
+
1456
+ it 'Metrics.to_context surfaces W1 MIX when generator_mix is unhealthy' do
1457
+ allow(reward).to receive(:generator_mix).and_return(
1458
+ n: 20, healthy: false, trajectory_fraction: 0.1,
1459
+ urgent: %w[critic counterfactual], suppress: %w[mistakes_resolve],
1460
+ recommendation: 'boost:critic,counterfactual'
1461
+ )
1462
+ # ensure at least one metrics row so to_context is non-empty
1463
+ metrics.record(name: 'shell', success: true, duration: 0.01) if metrics.respond_to?(:record)
1464
+ ctx = metrics.to_context(limit: 3)
1465
+ expect(ctx).to match(/W1 MIX:/)
1466
+ expect(ctx).to match(/boost:critic/)
1467
+ end
1468
+ end
1469
+
1470
+ describe 'P0 · Budget exhaust deepen (last-iter / no-CF-hot / exhaust Learning)' do
1471
+ it 'tightens budget-hot max_iters caps to 8 local / 12 remote' do
1472
+ src = File.read(loop_mod.method(:run).source_location.first)
1473
+ # max_iters is private_class_method — read surrounding source
1474
+ expect(src).to match(/ollama \? 8 : 12/)
1475
+ expect(src).to match(/budget_exhaustion_hot\?/)
1476
+ end
1477
+
1478
+ it 'forces tools=nil on last iter and skips counterfactual when budget-hot' do
1479
+ src = File.read(loop_mod.method(:run).source_location.first)
1480
+ expect(src).to match(/FINAL ITERATION/)
1481
+ expect(src).to match(/last_iter \? nil : tools/)
1482
+ # CF gate requires !budget_exhaustion_hot?
1483
+ expect(src).to match(/!budget_exhaustion_hot\?/)
1484
+ expect(src).to match(/Curriculum\.counterfactual/)
1485
+ end
1486
+
1487
+ it 'exhaust path appends session + auto_introspect (not bare string only)' do
1488
+ src = File.read(loop_mod.method(:run).source_location.first)
1489
+ idx = src.index('shape: :budget_exhausted')
1490
+ expect(idx).not_to be_nil
1491
+ tail = src[idx, 800]
1492
+ expect(tail).to match(/append_session/)
1493
+ expect(tail).to match(/auto_introspect/)
1494
+ expect(tail).to match(/final_msg/)
1495
+ end
1496
+
1497
+ it 'STATUS table lists Budget exhaust deepen' do
1498
+ doc = File.read(File.expand_path('../../documentation/Reinforcement-Learning.md', __dir__))
1499
+ expect(doc).to match(/Budget exhaust deepen/)
1500
+ expect(doc).to match(/Last-iter force-final/)
1501
+ end
1502
+ end
1204
1503
  end
1205
1504
  # rubocop:enable Metrics/BlockLength
@@ -1,10 +1,48 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'spec_helper'
4
+ require 'json'
4
5
 
5
6
  describe 'PWN::AI::Agent::Tools ruby_eval' do
6
- it 'registers the pwn_eval tool' do
7
+ before(:all) do
7
8
  PWN::AI::Agent::Registry.discover
9
+ end
10
+
11
+ it 'registers the pwn_eval tool' do
8
12
  expect(PWN::AI::Agent::Registry.lookup(name: 'pwn_eval')).not_to be_nil
9
13
  end
14
+
15
+ it 'returns structured error for SyntaxError payloads (constant block param)' do
16
+ entry = PWN::AI::Agent::Registry.lookup(name: 'pwn_eval')
17
+ result = entry.handler.call(code: 'results = probes.map do |R|')
18
+ expect(result).to be_a(Hash)
19
+ expect(result[:error]).to match(/SyntaxError/)
20
+ expect(result[:error]).to match(/\(pwn_eval\)/)
21
+ expect(result[:error]).not_to match(/ruby_eval\.rb:49/)
22
+ expect(result[:hint]).to match(/\|r\| not \|R\|/)
23
+ end
24
+
25
+ it 'evaluates valid code and returns stdout + value' do
26
+ entry = PWN::AI::Agent::Registry.lookup(name: 'pwn_eval')
27
+ result = entry.handler.call(code: '1 + 1')
28
+ expect(result[:error]).to be_nil
29
+ expect(result[:value]).to eq('2')
30
+ end
31
+
32
+ it 'Dispatch.call JSON-wraps syntax errors instead of raising' do
33
+ out = PWN::AI::Agent::Dispatch.call(
34
+ tool_call: {
35
+ id: 't1',
36
+ type: 'function',
37
+ function: {
38
+ name: 'pwn_eval',
39
+ arguments: JSON.generate(code: 'results = probes.map do |R|')
40
+ }
41
+ }
42
+ )
43
+ parsed = JSON.parse(out, symbolize_names: true)
44
+ # handler now rescues → success:true with error key in result
45
+ expect(parsed[:success]).to eq(true)
46
+ expect(parsed[:result][:error]).to match(/SyntaxError/)
47
+ end
10
48
  end
@@ -20,7 +20,7 @@
20
20
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.build_eval_set Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.build_eval_set`: "}]}
21
21
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.build_telemetry Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.build_telemetry`: "}]}
22
22
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.bump_cooldown! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.bump_cooldown!`: "}]}
23
- {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.calibrate Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.calibrate`: Supported Method Parameters\n\nPWN::AI::Agent::Curriculum.calibrate(predicted:, actual:, engine:)\n"}]}
23
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.calibrate Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.calibrate`: "}]}
24
24
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.counterfactual Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.counterfactual`: "}]}
25
25
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.critic Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.critic`: Supported Method Parameters\n\nv = PWN::AI::Agent::Curriculum.critic(\n\nrequest: 'required - user request',\nfinal: 'required - candidate final answer',\nsession_id: 'optional - for evidence lookup'\n\n)\n\nReturns { verdict: :pass|:flaw, flaw:, confidence: }. On :flaw the (final, flaw) pair is recorded as a preference so a future self-correction becomes DPO signal.\n"}]}
26
26
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.critic_text_only Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.critic_text_only`: "}]}
@@ -39,12 +39,14 @@
39
39
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.offline_judge Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.offline_judge`: Supported Method Parameters\n\nr = PWN::AI::Agent::Curriculum.offline_judge(\n\nsince_hours: 'optional - lookback window (default 24)',\nlimit: 'optional - max sessions to score (default 40)',\nprm: 'optional - also run Process Reward Model (default true)',\ncommit: 'optional - write scores into learning/sentinel (default true)'\n\n)\n"}]}
40
40
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.ollama_create Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.ollama_create`: "}]}
41
41
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.practice Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.practice`: "}]}
42
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.practice_kpi Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.practice_kpi`: "}]}
42
43
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.practice_skip? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.practice_skip?`: "}]}
43
44
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.preference_balance Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.preference_balance`: "}]}
44
45
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.preference_diet_gate Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.preference_diet_gate`: "}]}
45
46
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.python_site_package_roots Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.python_site_package_roots`: "}]}
46
47
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.red_team_plan Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.red_team_plan`: Supported Method Parameters\n\nhint = PWN::AI::Agent::Curriculum.red_team_plan(\n\nrequest: 'required - user goal',\nplan: 'required - numbered plan text from plan_first'\n\n)\n"}]}
47
48
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.reflect_available? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.reflect_available?`: "}]}
49
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.repeating_trend Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.repeating_trend`: "}]}
48
50
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.replay_on Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.replay_on`: "}]}
49
51
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.replay_on_detailed Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.replay_on_detailed`: "}]}
50
52
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Curriculum.revise_after_flaw Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Curriculum.revise_after_flaw`: "}]}
@@ -258,6 +260,7 @@
258
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"}]}
259
261
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.auto_introspect_enabled? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.auto_introspect_enabled?`: "}]}
260
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.checkable_claim? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.checkable_claim?`: "}]}
261
264
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.compress_exemplar Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.compress_exemplar`: "}]}
262
265
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.compress_finetune_trace Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.compress_finetune_trace`: "}]}
263
266
  {"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"}]}
@@ -265,7 +268,7 @@
265
268
  {"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"}]}
266
269
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.export_finetune Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.export_finetune`: "}]}
267
270
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.fact_check_local_final Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.fact_check_local_final`: "}]}
268
- {"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.flip_last_outcome Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.flip_last_outcome`: Supported Method Parameters\n\nPWN::AI::Agent::Learning.flip_last_outcome(\n\nsession_id: 'optional - only flip if the newest outcome belongs to this session',\nreason: 'optional - why it is being flipped (usually the user correction text)'\n\n)\n\nRewrites the most-recently-appended learning.jsonl entry from success:true to success:false. Called by Mistakes.check_user_correction when the user’s next message rejects the previous answer, so the 100 %-success illusion is broken and the failure enters the corpus.\n"}]}
271
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.flip_last_outcome Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.flip_last_outcome`: "}]}
269
272
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.fold_judge_into_metrics Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.fold_judge_into_metrics`: "}]}
270
273
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.help Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.help`: "}]}
271
274
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Learning.heuristic_lessons Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Learning.heuristic_lessons`: "}]}
@@ -316,9 +319,11 @@
316
319
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Metrics.effective_rate Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Metrics.effective_rate`: "}]}
317
320
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Metrics.gamma_sample Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Metrics.gamma_sample`: "}]}
318
321
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Metrics.help Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Metrics.help`: "}]}
322
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Metrics.judge_confidence Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Metrics.judge_confidence`: "}]}
319
323
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Metrics.judge_rate Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Metrics.judge_rate`: "}]}
320
324
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Metrics.load Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Metrics.load`: Supported Method Parameters\n\nmetrics = PWN::AI::Agent::Metrics.load\n"}]}
321
325
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Metrics.prm_advantage Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Metrics.prm_advantage`: "}]}
326
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Metrics.prm_n Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Metrics.prm_n`: "}]}
322
327
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Metrics.proxy_trust Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Metrics.proxy_trust`: "}]}
323
328
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Metrics.record Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Metrics.record`: Supported Method Parameters\n\nPWN::AI::Agent::Metrics.record(\n\nname: 'required - tool name that was dispatched',\nsuccess: 'required - Boolean, did the handler complete without error',\nduration: 'optional - Float seconds the dispatch took',\nerror: 'optional - String error message when success is false',\nengine: 'optional - Symbol/String AI engine that chose this tool (segments telemetry)'\n\n)\n"}]}
324
329
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Metrics.record_calibration Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Metrics.record_calibration`: Supported Method Parameters\n\nPWN::AI::Agent::Metrics.record_calibration(predicted:, actual:, brier:, engine:)\n\nW3 — plan_first emits p(success); Loop.run calls this with the realised outcome. Tracked per-engine so calibration of the local LoRA vs frontier is comparable.\n"}]}
@@ -399,9 +404,11 @@
399
404
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Reward.export_dpo Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Reward.export_dpo`: "}]}
400
405
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Reward.extract_cmd Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Reward.extract_cmd`: "}]}
401
406
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Reward.fold_step_reward_to_metrics Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Reward.fold_step_reward_to_metrics`: "}]}
407
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Reward.generator_mix Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Reward.generator_mix`: "}]}
402
408
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Reward.help Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Reward.help`: "}]}
403
409
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Reward.heuristic_judge Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Reward.heuristic_judge`: "}]}
404
410
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Reward.heuristic_prm Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Reward.heuristic_prm`: "}]}
411
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Reward.infer_shape Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Reward.infer_shape`: "}]}
405
412
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Reward.judge Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Reward.judge`: Supported Method Parameters\n\nv = PWN::AI::Agent::Reward.judge(\n\nrequest: 'required - original user request',\nfinal: 'required - assistant final answer',\nsession_id: 'optional - PWN::Sessions id (adds tool trace)',\ntrace: 'optional - Array of tool-result strings (overrides session_id)',\ncommit: 'optional - write score into learning.jsonl / sentinel (default true)'\n\n)\n"}]}
406
413
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Reward.llm_judge Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Reward.llm_judge`: "}]}
407
414
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Reward.llm_prm Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Reward.llm_prm`: "}]}
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.647
4
+ version: 0.5.650
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.