pwn 0.5.766 → 0.5.772

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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -3
  3. data/documentation/Configuration.md +2 -0
  4. data/documentation/General-PWN-Usage.md +68 -0
  5. data/documentation/Harness-Evaluation.md +312 -0
  6. data/documentation/pwn-ai-Agent.md +135 -6
  7. data/etc/default_cron/session_cleanup.rb +6 -1
  8. data/etc/default_skills/pwn/ai/agent/dispatch/SKILL.md +1 -0
  9. data/etc/default_skills/pwn/ai/agent/open_goal/SKILL.md +1 -0
  10. data/etc/default_skills/pwn/ai/agent/skill_review/SKILL.md +2 -0
  11. data/etc/default_skills/pwn/ai/agent/solve/SKILL.md +4 -2
  12. data/etc/default_skills/pwn/ai/context_ingestion/SKILL.md +1 -1
  13. data/etc/default_skills/pwn/ai/http_retry/SKILL.md +1 -0
  14. data/etc/default_skills/pwn/plugins/repl/ai_console/SKILL.md +1 -1
  15. data/etc/default_skills/pwn/plugins/repl/mesh/SKILL.md +4 -0
  16. data/etc/default_skills/pwn/plugins/repl/mesh/references/urls.md +3 -0
  17. data/etc/default_skills/pwn/redaction/SKILL.md +1 -1
  18. data/etc/default_skills/pwn/runtime_fingerprint/SKILL.md +47 -0
  19. data/lib/pwn/ai/agent/dispatch.rb +50 -6
  20. data/lib/pwn/ai/agent/loop.rb +192 -115
  21. data/lib/pwn/ai/agent/open_goal.rb +43 -1
  22. data/lib/pwn/ai/agent/registry.rb +6 -1
  23. data/lib/pwn/ai/agent/request_runtime.rb +508 -17
  24. data/lib/pwn/ai/agent/result.rb +23 -6
  25. data/lib/pwn/ai/agent/reward.rb +4 -1
  26. data/lib/pwn/ai/agent/skill_review.rb +148 -1
  27. data/lib/pwn/ai/agent/solve.rb +420 -39
  28. data/lib/pwn/ai/agent/swarm.rb +11 -2
  29. data/lib/pwn/ai/agent/tools/shell.rb +23 -0
  30. data/lib/pwn/ai/agent/turn_finalizer.rb +37 -6
  31. data/lib/pwn/ai/agent/verification.rb +42 -5
  32. data/lib/pwn/ai/anthropic.rb +59 -5
  33. data/lib/pwn/ai/cli.rb +48 -4
  34. data/lib/pwn/ai/context.rb +1 -1
  35. data/lib/pwn/ai/context_ingestion.rb +16 -1
  36. data/lib/pwn/ai/gemini.rb +36 -5
  37. data/lib/pwn/ai/grok.rb +19 -15
  38. data/lib/pwn/ai/http_retry.rb +14 -0
  39. data/lib/pwn/ai/mcp/combo_nation.rb +5 -2
  40. data/lib/pwn/ai/mcp.rb +5 -2
  41. data/lib/pwn/ai/ollama.rb +46 -5
  42. data/lib/pwn/ai/open_ai.rb +11 -11
  43. data/lib/pwn/ai/open_web_ui.rb +46 -5
  44. data/lib/pwn/config.rb +10 -2
  45. data/lib/pwn/migrate.rb +9 -1
  46. data/lib/pwn/plugins/repl/ai.rb +31 -1
  47. data/lib/pwn/plugins/repl/ai_console.rb +843 -19
  48. data/lib/pwn/plugins/repl/ai_console_commands.rb +23 -5
  49. data/lib/pwn/plugins/repl/ai_swarm.rb +65 -5
  50. data/lib/pwn/plugins/repl/mesh.rb +894 -83
  51. data/lib/pwn/plugins/repl.rb +2 -0
  52. data/lib/pwn/redaction.rb +29 -5
  53. data/lib/pwn/runtime_fingerprint.rb +84 -0
  54. data/lib/pwn/version.rb +1 -1
  55. data/lib/pwn.rb +1 -0
  56. data/spec/fixtures/harness/adapters.json +62 -0
  57. data/spec/fixtures/harness/adapters.json.sha256 +1 -0
  58. data/spec/fixtures/harness/experiment_gold.json +9 -0
  59. data/spec/fixtures/harness/experiment_gold.json.sha256 +1 -0
  60. data/spec/fixtures/harness/experiment_tasks.json +12 -0
  61. data/spec/fixtures/harness/experiment_tasks.json.sha256 +1 -0
  62. data/spec/fixtures/harness/live_comparison.json +21 -0
  63. data/spec/fixtures/harness/live_comparison.json.sha256 +1 -0
  64. data/spec/fixtures/harness/manifest.json +290 -0
  65. data/spec/fixtures/harness/manifest.json.sha256 +1 -0
  66. data/spec/integration/harness_evaluation_spec.rb +171 -0
  67. data/spec/integration/harness_layout_spec.rb +15 -0
  68. data/spec/integration/harness_live_spec.rb +26 -0
  69. data/spec/integration/harness_package_spec.rb +38 -0
  70. data/spec/integration/native_ruby_helpers_spec.rb +4 -2
  71. data/spec/integration/reinforced_feedback_loop_spec.rb +2 -2
  72. data/spec/lib/pwn/ai/agent/dispatch_spec.rb +47 -3
  73. data/spec/lib/pwn/ai/agent/loop_spec.rb +479 -17
  74. data/spec/lib/pwn/ai/agent/open_goal_spec.rb +17 -0
  75. data/spec/lib/pwn/ai/agent/registry_spec.rb +10 -0
  76. data/spec/lib/pwn/ai/agent/request_runtime_spec.rb +232 -0
  77. data/spec/lib/pwn/ai/agent/result_spec.rb +33 -0
  78. data/spec/lib/pwn/ai/agent/signal_hygiene_spec.rb +1 -1
  79. data/spec/lib/pwn/ai/agent/skill_review_spec.rb +17 -5
  80. data/spec/lib/pwn/ai/agent/solve_spec.rb +392 -7
  81. data/spec/lib/pwn/ai/agent/swarm_spec.rb +19 -0
  82. data/spec/lib/pwn/ai/agent/tools/ruby_eval_spec.rb +2 -2
  83. data/spec/lib/pwn/ai/agent/turn_finalizer_spec.rb +20 -0
  84. data/spec/lib/pwn/ai/agent/verification_spec.rb +12 -0
  85. data/spec/lib/pwn/ai/anthropic_spec.rb +10 -0
  86. data/spec/lib/pwn/ai/cli_spec.rb +40 -0
  87. data/spec/lib/pwn/ai/context_ingestion_spec.rb +12 -0
  88. data/spec/lib/pwn/ai/grok_spec.rb +35 -0
  89. data/spec/lib/pwn/ai/mcp/combo_nation_spec.rb +10 -0
  90. data/spec/lib/pwn/ai/mcp_spec.rb +10 -0
  91. data/spec/lib/pwn/ai/open_ai_spec.rb +13 -0
  92. data/spec/lib/pwn/migrate_spec.rb +37 -4
  93. data/spec/lib/pwn/plugins/repl/ai_console_completion_wrap_spec.rb +110 -0
  94. data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +307 -3
  95. data/spec/lib/pwn/plugins/repl/ai_swarm_spec.rb +29 -0
  96. data/spec/lib/pwn/plugins/repl/ai_vault_edit_spec.rb +221 -0
  97. data/spec/lib/pwn/plugins/repl_mesh_channel_identity_spec.rb +134 -0
  98. data/spec/lib/pwn/plugins/repl_mesh_dispatch_spec.rb +2 -0
  99. data/spec/lib/pwn/plugins/repl_mesh_dm_transport_spec.rb +2 -1
  100. data/spec/lib/pwn/plugins/repl_mesh_multi_rx_spec.rb +315 -0
  101. data/spec/lib/pwn/plugins/repl_mesh_origin_gps_spec.rb +120 -0
  102. data/spec/lib/pwn/plugins/repl_mesh_rx_errors_spec.rb +325 -0
  103. data/spec/lib/pwn/plugins/repl_mesh_scroll_spec.rb +49 -0
  104. data/spec/lib/pwn/plugins/repl_mesh_transport_label_spec.rb +43 -15
  105. data/spec/lib/pwn/plugins/repl_spec.rb +31 -7
  106. data/spec/lib/pwn/redaction_spec.rb +17 -0
  107. data/spec/lib/pwn/runtime_fingerprint_spec.rb +53 -0
  108. data/spec/lib/pwn/session_cleanup_default_spec.rb +1 -1
  109. data/spec/spec_helper.rb +3 -1
  110. data/spec/support/harness/cli_faults.rb +112 -0
  111. data/spec/support/harness/evaluate.rb +191 -0
  112. data/spec/support/harness/experiment_preload.rb +20 -0
  113. data/spec/support/harness/experiments.rb +137 -0
  114. data/spec/support/harness/fake_provider.rb +82 -0
  115. data/spec/support/harness/fault_preload.rb +44 -0
  116. data/spec/support/harness/package_acceptance.rb +77 -0
  117. data/spec/support/harness/record_training.rb +18 -0
  118. data/spec/support/harness/reference_adapter.py +128 -0
  119. data/spec/support/harness/runtime_faults.rb +97 -0
  120. data/spec/support/harness/session_acceptance.rb +70 -0
  121. data/spec/support/harness/swarm_acceptance.rb +61 -0
  122. data/third_party/pwn_rdoc.jsonl +129 -3
  123. metadata +46 -8
@@ -35,6 +35,7 @@ PWN::AI::Agent::OpenGoal.goal_file(opts)
35
35
 
36
36
  - `goal_file`
37
37
  - `current`
38
+ - `for_session`
38
39
  - `begin`
39
40
  - `clear`
40
41
  - `resume`
@@ -36,6 +36,8 @@ PWN::AI::Agent::SkillReview.review(opts)
36
36
  - `review`
37
37
  - `review_turn`
38
38
  - `note_reuse`
39
+ - `decide`
40
+ - `evidence_transcript`
39
41
  - `authors`
40
42
  - `help`
41
43
 
@@ -28,11 +28,13 @@ Class methods take `(opts = {})` and read `opts`.
28
28
 
29
29
  ```ruby
30
30
  PWN::AI::Agent::Solve.help
31
- PWN::AI::Agent::Solve.help(opts)
31
+ PWN::AI::Agent::Solve.apply_patch(opts)
32
32
  ```
33
33
 
34
34
  ## Public methods
35
35
 
36
+ - `apply_patch`
37
+ - `cleanup_worktree`
36
38
  - `authors`
37
39
  - `help`
38
40
 
@@ -42,5 +44,5 @@ PWN::AI::Agent::Solve.help(opts)
42
44
 
43
45
  ## Verification
44
46
 
45
- `PWN::AI::Agent::Solve.respond_to?(:authors)` after the
47
+ `PWN::AI::Agent::Solve.respond_to?(:apply_patch)` after the
46
48
  module is loaded. Read the source for parameter names.
@@ -12,7 +12,7 @@ metadata:
12
12
 
13
13
  # PWN::AI::Context
14
14
 
15
- Local evidence ingestion; unavailable embeddings never become synthetic vectors.
15
+ Local evidence ingestion; XML is bounded to 100 element levels before XPath. Unavailable embeddings never become synthetic vectors.
16
16
 
17
17
  ## When to use
18
18
 
@@ -35,6 +35,7 @@ PWN::AI::HttpRetry.timeout_s(opts)
35
35
 
36
36
  - `timeout_s`
37
37
  - `max_attempts`
38
+ - `provider_config`
38
39
  - `retryable`
39
40
  - `retry_after_s`
40
41
  - `quota_exhausted`
@@ -12,7 +12,7 @@ metadata:
12
12
 
13
13
  # PWN::Plugins::REPL::AIConsole
14
14
 
15
- Single-owner fullscreen agent console. Workers never paint the terminal.
15
+ Single-owner fullscreen console: wrapped completion and private /vault edit; Ctrl+S saves encrypted, Esc cancels.
16
16
 
17
17
  ## When to use
18
18
 
@@ -37,6 +37,10 @@ PWN::Plugins::REPL::Mesh.add_commands(opts)
37
37
  - `authors`
38
38
  - `help`
39
39
 
40
+ ## References
41
+
42
+ - `references/urls.md` — URLs from source
43
+
40
44
  ## Source
41
45
 
42
46
  `pwn/plugins/repl/mesh.rb`
@@ -0,0 +1,3 @@
1
+ # PWN::Plugins::REPL::Mesh source links
2
+
3
+ - https://maps.google.com/?q=[redacted]
@@ -12,7 +12,7 @@ metadata:
12
12
 
13
13
  # PWN::Redaction
14
14
 
15
- Shared write-boundary redaction. Never opens a credential store.
15
+ Write-boundary redaction; reject cyclic or over-100-level containers before inspecting secrets. Never opens a credential store.
16
16
 
17
17
  ## When to use
18
18
 
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: pwn-runtimefingerprint
3
+ description: Drive PWN::RuntimeFingerprint from pwn_eval.
4
+ license: MIT
5
+ allowed-tools: [pwn, pwn_eval]
6
+ metadata:
7
+ bundled: true
8
+ generated: true
9
+ module: PWN::RuntimeFingerprint
10
+ source: pwn/runtime_fingerprint.rb
11
+ ---
12
+
13
+ # PWN::RuntimeFingerprint
14
+
15
+ Read-only deployment diagnostics. A disk observation at compilation is not an attestation of VM bytecode, native extensions, or later monkey patches.
16
+
17
+ ## When to use
18
+
19
+ Call `PWN::RuntimeFingerprint` from `pwn_eval` when the task needs this module.
20
+ Do not reimplement it in shell.
21
+
22
+ ## Methodologies
23
+
24
+ Generated from `pwn/runtime_fingerprint.rb`. Prefer the public class methods below.
25
+ Class methods take `(opts = {})` and read `opts`.
26
+
27
+ ## How to call
28
+
29
+ ```ruby
30
+ PWN::RuntimeFingerprint.help
31
+ PWN::RuntimeFingerprint.report(opts)
32
+ ```
33
+
34
+ ## Public methods
35
+
36
+ - `report`
37
+ - `authors`
38
+ - `help`
39
+
40
+ ## Source
41
+
42
+ `pwn/runtime_fingerprint.rb`
43
+
44
+ ## Verification
45
+
46
+ `PWN::RuntimeFingerprint.respond_to?(:report)` after the
47
+ module is loaded. Read the source for parameter names.
@@ -51,6 +51,9 @@ module PWN
51
51
  entry = Registry.lookup(name: name) || Registry.lookup(name: repair_name(name: name))
52
52
  return JSON.generate(error: "unknown tool: #{name}") unless entry
53
53
 
54
+ owner = Thread.current[:pwn_request_runtime]
55
+ return JSON.generate(success: false, error: 'request_tool_permission_denied') if owner && !owner.tool_allowed?(name: entry.name, toolset: entry.toolset)
56
+
54
57
  solve_tools = Thread.current[:pwn_solve_tools]
55
58
  return JSON.generate(success: false, error: 'Tool denied by solve role capability boundary') if solve_tools && !solve_tools.include?(entry.name.to_s)
56
59
 
@@ -123,7 +126,12 @@ module PWN
123
126
 
124
127
  started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
125
128
  begin
126
- result = entry.handler.call(args)
129
+ runtime = Thread.current[:pwn_request_runtime]
130
+ result = if runtime
131
+ runtime.execute_tool(name: entry.name, args: args, effect: effect(name: entry.name, args: args), action_id: tool_call[:id] || tool_call['id']) { entry.handler.call(args) }
132
+ else
133
+ entry.handler.call(args)
134
+ end
127
135
  rescue StandardError => e
128
136
  finish_budget(budget: budget, result: { error: e.is_a?(Timeout::Error) ? 'timeout' : e.class.name }, elapsed: Process.clock_gettime(Process::CLOCK_MONOTONIC) - started)
129
137
  raise
@@ -138,6 +146,7 @@ module PWN
138
146
  result = JSON.parse(PWN::Plugins::Vault.redact(text: JSON.generate(result)))
139
147
  end
140
148
  response = { success: true, result: result, effect: effect(name: entry.name, args: args) }
149
+ response[:success] = false if result.is_a?(Hash) && (result[:error] || result['error'] || result[:success] == false || result['success'] == false)
141
150
  response[:budget] = telemetry if telemetry
142
151
  if telemetry && telemetry[:remaining_s].zero?
143
152
  response[:success] = false
@@ -207,6 +216,8 @@ module PWN
207
216
  private_class_method def self.prepare_budget(opts = {})
208
217
  entry = opts[:entry]
209
218
  args = opts[:args]
219
+ return nil if Thread.current[:pwn_request_runtime] && args.key?(:timeout)
220
+
210
221
  if entry.name == 'shell' && (args[:background] == true || (!args.key?(:timeout) && ToolGuard.auto_job?(payload: args[:command].to_s)))
211
222
  args[:background] = true
212
223
  return nil
@@ -299,15 +310,18 @@ module PWN
299
310
 
300
311
  private_class_method def self.drop_required(opts = {})
301
312
  node = opts[:node]
313
+ depth = opts.fetch(:depth, 0)
314
+ raise JSON::NestingError, 'tool schema exceeds JSON nesting limit (100)' if depth >= 100 && (node.is_a?(Hash) || node.is_a?(Array))
315
+
302
316
  case node
303
317
  when Hash
304
318
  node.each_with_object({}) do |(key, value), acc|
305
319
  next if key.to_s == 'required'
306
320
 
307
- acc[key] = drop_required(node: value)
321
+ acc[key] = drop_required(node: value, depth: depth + 1)
308
322
  end
309
323
  when Array
310
- node.map { |value| drop_required(node: value) }
324
+ node.map { |value| drop_required(node: value, depth: depth + 1) }
311
325
  else
312
326
  node
313
327
  end
@@ -514,9 +528,34 @@ module PWN
514
528
  learning_note_outcome skill_create skill_add_reference skills_update
515
529
  ].freeze
516
530
 
531
+ # Audited local reads only. Resource conflicts deliberately serialize
532
+ # even two reads of one handle, and every other tool is a barrier.
533
+ PARALLEL_READS = %w[artifact_read artifact_grep artifacts_get artifact_get job_tail].freeze
534
+
535
+ # Supported Method Parameters::
536
+ # metadata = PWN::AI::Agent::Dispatch.scheduling(
537
+ # tool_call: 'required - tool call whose audited read resources are resolved'
538
+ # )
539
+ public_class_method def self.scheduling(opts = {})
540
+ call = opts[:tool_call]
541
+ name = call.dig(:function, :name).to_s
542
+ args = call.dig(:function, :arguments)
543
+ args = JSON.parse(args) if args.is_a?(String)
544
+ args = args.transform_keys(&:to_s)
545
+ resource = args['id'] if name == 'job_tail'
546
+ if PARALLEL_READS.include?(name) && name != 'job_tail'
547
+ handle = args['handle'] || args['sha256']
548
+ path = handle ? PWN::Plugins::ArtifactRegistry.resolve(handle: handle)[:path] : args['path'] || args['ref']
549
+ resource = File.realpath(path) if path
550
+ end
551
+ { exclusive: !PARALLEL_READS.include?(name) || resource.to_s.empty?, resources: ["handle:#{resource}"] }
552
+ rescue JSON::ParserError, NoMethodError, ArgumentError, SystemCallError
553
+ { exclusive: true, resources: ['*'] }
554
+ end
555
+
517
556
  public_class_method def self.effect(opts = {})
518
557
  name = opts[:name].to_s
519
- return :read if name.empty?
558
+ return :read if PARALLEL_READS.include?(name)
520
559
  return :recall if RECALL_TOOLS.include?(name)
521
560
  return :store if STORE_TOOLS.include?(name)
522
561
 
@@ -525,9 +564,9 @@ module PWN
525
564
  return :browse if blob.match?(BROWSE_ARGV_RX)
526
565
  return :eval if name == 'pwn_eval'
527
566
 
528
- :read
567
+ :exclusive
529
568
  rescue StandardError
530
- :read
569
+ :exclusive
531
570
  end
532
571
 
533
572
  private_class_method def self.argv_blob(opts = {})
@@ -632,6 +671,11 @@ module PWN
632
671
  call: 'optional - shell{command: uname -s} / tool:shell{command:id}'
633
672
  )
634
673
 
674
+ # Resolve audited read resources; unknown tools are exclusive barriers.
675
+ #{self}.scheduling(
676
+ tool_call: 'required - tool call with function name and arguments'
677
+ )
678
+
635
679
  # Run effect and return its result
636
680
  #{self}.effect(
637
681
  name: 'required - binary or identifier name',