pwn 0.5.643 → 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.
@@ -39,19 +39,42 @@ PWN::AI::Agent::Registry.register(
39
39
  # INTENTIONAL: this IS the pwn-ai → PWN bridge
40
40
  # As YTCracker says, "It ain't a bug, it's a featcha."
41
41
  # https://www.youtube.com/watch?v=2nALqqSqdDw
42
- # val = eval(code, TOPLEVEL_BINDING, '(pwn_eval)')
42
+ #
43
+ # File label '(pwn_eval)' (not __FILE__) so SyntaxError / NameError
44
+ # messages cite the *payload* line, not ruby_eval.rb:49 glued to source
45
+ # (e.g. "ruby_eval:49results = probes.map do |R|").
46
+ # Rescue ScriptError (SyntaxError, LoadError, NotImplementedError) as
47
+ # well as StandardError — Dispatch only catches StandardError, so an
48
+ # unrescued SyntaxError previously escaped the tool loop. Returning a
49
+ # structured error lets the model self-heal (fix code and retry).
50
+ # rubocop:disable Style/EvalWithLocation -- intentional file label
51
+ # '(pwn_eval)' so SyntaxError/NameError messages cite the payload, not
52
+ # this library line (specs + model self-heal depend on that string).
43
53
  proc = eval(
44
- "proc { #{code} }",
54
+ "proc { #{code}\n}",
45
55
  TOPLEVEL_BINDING,
46
- __FILE__,
47
- __LINE__ - 3
56
+ '(pwn_eval)',
57
+ 1
48
58
  )
59
+ # rubocop:enable Style/EvalWithLocation
49
60
  val = proc.call
50
61
  # rubocop:enable Security/Eval
51
62
  # rubocop:enable Style/DocumentDynamicEvalDefinition
52
63
  { stdout: buf.string, value: val.inspect }
53
-
54
- # TODO: A rescue here may enable self-healing of the agent if the model emits code that raises an exception. The model could then be prompted to fix the code and try again.
64
+ rescue ScriptError, StandardError => e
65
+ {
66
+ stdout: buf.string,
67
+ error: "#{e.class}: #{e.message}",
68
+ backtrace: Array(e.backtrace).first(5),
69
+ # Hint common payload footguns the model keeps emitting
70
+ hint: (
71
+ if e.is_a?(SyntaxError) && e.message.match?(/formal argument cannot be a constant/)
72
+ 'Block parameters must be local variables (lowercase), e.g. |r| not |R|. Also close every do/end.'
73
+ elsif e.is_a?(SyntaxError)
74
+ 'Payload failed to parse. Check matching do/end, braces, and that no line-number prefix was glued onto the code.'
75
+ end
76
+ )
77
+ }
55
78
  ensure
56
79
  $stdout = old_stdout
57
80
  end
data/lib/pwn/cron.rb CHANGED
@@ -231,8 +231,11 @@ module PWN
231
231
  )
232
232
  end
233
233
 
234
- # P3 — backfill ORM/PRM labels when local :failure_only introspect is on
235
- unless names.include?('curriculum_offline_judge')
234
+ # P3 — backfill ORM/PRM labels when local :failure_only introspect is on.
235
+ # P13 — treat legacy alias offline_judge_nightly as the same job so
236
+ # install_defaults never double-seeds the 30 3 * * * slot.
237
+ offline_names = %w[curriculum_offline_judge offline_judge_nightly]
238
+ unless names.intersect?(offline_names)
236
239
  seeded << create(
237
240
  name: 'curriculum_offline_judge',
238
241
  schedule: '30 3 * * *',
@@ -241,6 +244,15 @@ module PWN
241
244
  enabled: true
242
245
  )
243
246
  end
247
+ # Disable duplicate alias if both exist (idempotent cleanup).
248
+ if names.include?('curriculum_offline_judge') && names.include?('offline_judge_nightly')
249
+ begin
250
+ dup = jobs.values.find { |j| j[:name].to_s == 'offline_judge_nightly' }
251
+ disable(id: dup[:id]) if dup && dup[:enabled]
252
+ rescue StandardError
253
+ nil
254
+ end
255
+ end
244
256
 
245
257
  # M1/M3 — nightly memory GC so the injected MEMORY block stays high-signal
246
258
  unless names.include?('learning_consolidate_nightly')
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.643'
4
+ VERSION = '0.5.650'
5
5
  end