pwn 0.5.766 → 0.5.771

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 (122) 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 +122 -0
  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 +16 -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 +9 -3
  37. data/lib/pwn/ai/grok.rb +10 -5
  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 +7 -3
  42. data/lib/pwn/ai/open_ai.rb +11 -11
  43. data/lib/pwn/ai/open_web_ui.rb +7 -3
  44. data/lib/pwn/config.rb +6 -2
  45. data/lib/pwn/migrate.rb +5 -1
  46. data/lib/pwn/plugins/repl/ai.rb +15 -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 +390 -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/mcp/combo_nation_spec.rb +10 -0
  89. data/spec/lib/pwn/ai/mcp_spec.rb +10 -0
  90. data/spec/lib/pwn/ai/open_ai_spec.rb +13 -0
  91. data/spec/lib/pwn/migrate_spec.rb +37 -4
  92. data/spec/lib/pwn/plugins/repl/ai_console_completion_wrap_spec.rb +110 -0
  93. data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +307 -3
  94. data/spec/lib/pwn/plugins/repl/ai_swarm_spec.rb +29 -0
  95. data/spec/lib/pwn/plugins/repl/ai_vault_edit_spec.rb +221 -0
  96. data/spec/lib/pwn/plugins/repl_mesh_channel_identity_spec.rb +134 -0
  97. data/spec/lib/pwn/plugins/repl_mesh_dispatch_spec.rb +2 -0
  98. data/spec/lib/pwn/plugins/repl_mesh_dm_transport_spec.rb +2 -1
  99. data/spec/lib/pwn/plugins/repl_mesh_multi_rx_spec.rb +315 -0
  100. data/spec/lib/pwn/plugins/repl_mesh_origin_gps_spec.rb +120 -0
  101. data/spec/lib/pwn/plugins/repl_mesh_rx_errors_spec.rb +325 -0
  102. data/spec/lib/pwn/plugins/repl_mesh_scroll_spec.rb +49 -0
  103. data/spec/lib/pwn/plugins/repl_mesh_transport_label_spec.rb +43 -15
  104. data/spec/lib/pwn/plugins/repl_spec.rb +6 -5
  105. data/spec/lib/pwn/redaction_spec.rb +17 -0
  106. data/spec/lib/pwn/runtime_fingerprint_spec.rb +53 -0
  107. data/spec/lib/pwn/session_cleanup_default_spec.rb +1 -1
  108. data/spec/spec_helper.rb +3 -1
  109. data/spec/support/harness/cli_faults.rb +107 -0
  110. data/spec/support/harness/evaluate.rb +176 -0
  111. data/spec/support/harness/experiment_preload.rb +20 -0
  112. data/spec/support/harness/experiments.rb +137 -0
  113. data/spec/support/harness/fake_provider.rb +80 -0
  114. data/spec/support/harness/fault_preload.rb +44 -0
  115. data/spec/support/harness/package_acceptance.rb +77 -0
  116. data/spec/support/harness/record_training.rb +18 -0
  117. data/spec/support/harness/reference_adapter.py +128 -0
  118. data/spec/support/harness/runtime_faults.rb +97 -0
  119. data/spec/support/harness/session_acceptance.rb +64 -0
  120. data/spec/support/harness/swarm_acceptance.rb +61 -0
  121. data/third_party/pwn_rdoc.jsonl +120 -1
  122. metadata +46 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48f9dad90543bf3140d52f14058c9b59c0e011daa42b8729ff4614fb01b34d60
4
- data.tar.gz: e04116b2e14a9f728a1a583762ad9395f4858dcad190211c05988e8e5d60830b
3
+ metadata.gz: 4a5ecce60b421a59e80f691710dd5ae25cd79d083b59207b25baccf8fe8e5b35
4
+ data.tar.gz: 725ceb379d5436565b707e29d26e61e9cc10262b31e7cec537aec0e210d77fea
5
5
  SHA512:
6
- metadata.gz: e10732fcb06f209fd72fa0518241143fb9dccd448fdbf75c0d78d0da155ef0206f2bd068906e4be0b8605e1a15d73a7f1844f1a10b9f756f0cc34ff3b788495d
7
- data.tar.gz: 734bef29b1fb747424b676a0277095175b37816b6b7ca272dd3ff720ec36872ab69a7700afc10f74ba6542c7855282a97666ae6127e1bf8ccc66c78b8a5c3b1c
6
+ metadata.gz: b94c6d9ca4ebb2cf3835ed1f6951a15eae48e3484966e272861105a1c361964fd90235731e8c9ccfd10cfa03af736b42d69edb990b17aed37cd1c1504954ed11
7
+ data.tar.gz: 39dd6eac404a8f55906c56f9dda74836bd9b934374eea9a8168d556b383ccfd69722b1a16debc893d20a0e58f37adfce54da11fef999a0666bf4f9a4f2258863
data/Gemfile CHANGED
@@ -20,7 +20,7 @@ gem 'base32', '0.3.4'
20
20
  gem 'bitcoin-ruby', '0.0.20'
21
21
  gem 'brakeman', '8.1.0'
22
22
  gem 'bson', '5.2.0'
23
- gem 'bundler', '>=4.0.22'
23
+ gem 'bundler', '>=4.0.23'
24
24
  gem 'bundler-audit', '>=0.9.3'
25
25
  gem 'bunny', '3.4.0'
26
26
  gem 'colorize', '1.1.0'
@@ -50,7 +50,7 @@ gem 'libusb', '0.8.0'
50
50
  gem 'luhn', '3.0.0'
51
51
  gem 'mail', '2.9.1'
52
52
  gem 'mcp', '1.7.0'
53
- gem 'meshtastic', '0.0.187'
53
+ gem 'meshtastic', '0.0.188'
54
54
  gem 'metasm', '1.0.6'
55
55
  gem 'mongo', '2.26.0'
56
56
  gem 'msfrpc-client', '1.1.2'
@@ -94,7 +94,7 @@ gem 'selenium-webdriver', '4.51.0'
94
94
  gem 'slack-ruby-client', '3.3.0'
95
95
  gem 'socksify', '1.8.1'
96
96
  gem 'spreadsheet', '1.3.5'
97
- gem 'sqlite3', '2.9.6'
97
+ gem 'sqlite3', '2.10.0'
98
98
  gem 'thin', '2.0.1'
99
99
  gem 'tty-prompt', '0.23.1'
100
100
  gem 'tty-spinner', '0.9.3'
@@ -234,6 +234,7 @@ plugins:
234
234
  topic: 2/e/# # MQTT topic filter to subscribe/publish for this channel.
235
235
  channel_num: 8 # Meshtastic channel index (slot number on the device).
236
236
  from: '!deadbeef' # Sender node id used on outbound packets. Optional - defaults to !<mqtt client_id>.
237
+ short_name: # Optional display name for that from node. When set, it replaces the last four bytes of from.
237
238
  PWN: # Example second (private) channel definition.
238
239
  psk: ... # Private channel pre-shared key (base64). Redacted.
239
240
  region: US/UT # LoRa region tag for this channel.
@@ -403,6 +404,7 @@ PWN::Config.refresh_env
403
404
  | `plugins.meshtastic.channel.<NAME>.topic` | String | - | `pwn-mesh` | MQTT topic filter to subscribe/publish (e.g. `2/e/#`). |
404
405
  | `plugins.meshtastic.channel.<NAME>.channel_num` | Integer | - | `pwn-mesh` | Meshtastic channel index. |
405
406
  | `plugins.meshtastic.channel.<NAME>.from` | String | `!<mqtt client_id>` | `pwn-mesh` | Sender node id used on outbound packets. |
407
+ | `plugins.meshtastic.channel.<NAME>.short_name` | String | - | `pwn-mesh` | Optional short name for that `from` node. A non-empty value is shown instead of the last four bytes of `from`. |
406
408
  | `plugins.shodan.api_key` | String | - | `PWN::Plugins::Shodan` | Shodan API key (passed as `api_key:` to `Shodan.*`). |
407
409
 
408
410
  ### `memory` / `sessions` / `cron`
@@ -51,6 +51,74 @@ history # what you've typed → copy into a driver
51
51
  | `welcome-banner` | Redraw a random `PWN::Banner` |
52
52
  | `toggle-pager` | Pry pager on/off |
53
53
 
54
+ ### Concurrent mesh reception and reply routing
55
+
56
+ `pwn-mesh` keeps the selected primary transport for manual TX and RX. A
57
+ nonempty `plugins.meshtastic.mqtt` configuration also opens an independent
58
+ MQTT receiver when the primary is Serial, Bluetooth, or TCP. Missing/empty
59
+ MQTT configuration makes no additional connection attempt; primary MQTT uses
60
+ only one subscription. MQTT connection failures leave the primary usable and
61
+ are reported in the UI. `/transport` reconnects both subscriptions; `/back`
62
+ closes them. Ctrl+T switches the active pane between COMPOSE and
63
+ CONVERSATION. COMPOSE keeps Up/Down history. CONVERSATION scrolls with
64
+ Up/Down one row, PgUp/PgDn a page, Home to the top, and End to the live
65
+ bottom. The live bottom is not row 0, so PgDn there stays put. Typing or
66
+ Enter returns to COMPOSE; Enter does not send while CONVERSATION is active.
67
+ The active title is reversed and marked active.
68
+
69
+ There is **no dual TX or automatic bridging**. Compose and `/msg` use only the
70
+ selected primary, including explicit node/channel targets. The implicit `/msg`
71
+ target remains the primary's last sender/channel; secondary MQTT arrivals do
72
+ not change it. Automatic AI replies instead use only the connection that
73
+ received the triggering message. A duplicate arriving over both links is
74
+ displayed once. If that packet is also seen on Serial, Bluetooth, or TCP, the
75
+ row is relabeled to that primary transport even when MQTT arrived first.
76
+ MQTT remains the label only when it is the sole observation. Automatic AI
77
+ replies still use the first owning connection, not the display label. Dedup
78
+ uses sender, packet ID, network/channel path, destination and payload (not
79
+ text alone), retaining at most 1000 packets for five minutes. No-ID packets
80
+ cannot be safely merged.
81
+
82
+ Each header ends with the sender's last known position. A valid fix is
83
+ `lat,lon >> https://maps.google.com/?q=lat,lon` on that same header. The pane
84
+ wraps on ` · `, and only at ` >> ` if the GPS field itself does not fit, so
85
+ the coordinate is not split. This is the compact Maps URL; a maps.app.goo.gl
86
+ code cannot be minted without a Google share request. Unknown, unset, or
87
+ out-of-range positions display `GPS N/A` and omit the URL. `0,0` is kept when
88
+ a real location source is present and omitted when the source is unset. The
89
+ value is the last position recorded for that sender, not a freshness guarantee.
90
+
91
+ MQTT TX uses `channel.<name>.from` when set, otherwise the connected radio's
92
+ node number, otherwise a node-shaped `mqtt.client_id`. It does not publish the
93
+ Meshtastic library default `!00000b0b` when one of those identities exists.
94
+ When that `from` value is not empty, the short name for that node is its last
95
+ four bytes, unless the same channel sets a non-empty `short_name`. Other
96
+ senders keep their own short names.
97
+
98
+ AI dispatch still requires the toggle, a private channel PSK, `ai_whitelist`,
99
+ and leading `@ai`. MQTT-origin shared-channel replies publish through that
100
+ MQTT connection only; radio-origin replies use that radio only. MQTT PKI DMs
101
+ remain unsupported: no AI DM dispatch, no radio fallback, and no plaintext or
102
+ legacy-PSK DM fallback. Closing/replacing the origin while AI is running leaves
103
+ its reply unsent. Host submission and local conversation display are not proof
104
+ of RF or peer delivery.
105
+
106
+ MQTT TX selects the resolved channel name explicitly as `channel_id` (for
107
+ example, `LongFast`); the Meshtastic encoder calculates its wire channel hash.
108
+ That name is not the radio's numeric slot or the legacy `channel_num` setting.
109
+ Multiple configured names may share a PSK without changing the reply channel.
110
+ Radio replies retain the received slot; an unnamed slot with an ambiguous PSK
111
+ needs an explicit `radio_index` mapping rather than choosing the first key.
112
+
113
+ AI conversations use durable `PWN::Sessions` transcripts scoped to the sender,
114
+ network/channel, private channel key and destination. Reconnecting or receiving
115
+ the same scope on another transport keeps that history; ordinary non-`@ai` RX
116
+ does not create an AI session. Session/runtime/provider errors are reported as
117
+ `AI failed`, separately from `TX failed` after a reply has been generated.
118
+ Broker EOF/protocol errors appear as bounded, generation-scoped UI notices;
119
+ they close the failed receiver without a Ruby thread crash dump. Use
120
+ `/transport` to open fresh subscriptions rather than replaying failed AI sends.
121
+
54
122
  ## A 60-second attack chain
55
123
 
56
124
  ```ruby
@@ -0,0 +1,312 @@
1
+ # Deployment fingerprints and fair harness evaluation
2
+
3
+ ## Deployment diagnosis
4
+
5
+ `PWN::RuntimeFingerprint.report(expected_root: '/intended/gem/lib')` is read-only.
6
+ The existing pwn-ai `/status` prints the report. The observer starts from `pwn.rb`,
7
+ before version/config loading. It reports PID, Linux boot ID/process start ticks
8
+ (or unavailable), observer timestamp, loaded version, activated gem root, actual
9
+ loaded Ruby source paths, SHA-256 observed on disk at compilation, and current
10
+ on-disk SHA-256. Changed or missing observed source and expected-root mismatches
11
+ require restart. Unknown pre-observer bytes are **not** reported as a match.
12
+
13
+ These are disk observations at compilation, **not VM bytecode attestation**.
14
+ Concurrent replacement during compilation, native extensions, evaluated strings,
15
+ and later monkey patches cannot be attested. `restart_required: false` means no
16
+ known mismatch, not proof every component is current. `pwn.rb` and the observer
17
+ itself were compiled before observation and intentionally remain unknown.
18
+
19
+ Preserve the original request, session ID, request checkpoint and evidence first.
20
+ Rebuild/install the intended tree, restart the process, and recheck the report.
21
+ Do not hot-reload an active request or equate restoring a checkpoint with safely
22
+ replaying a side effect. No automatic deployment or recovery mutation occurs.
23
+
24
+ ## Offline acceptance (no credentials or provider calls)
25
+
26
+ ```sh
27
+ HOME=$(mktemp -d /tmp/pwn-rspec-home.XXXXXX) bundle exec rspec \
28
+ spec/integration/harness_evaluation_spec.rb \
29
+ spec/integration/harness_package_spec.rb \
30
+ spec/integration/harness_layout_spec.rb
31
+ ```
32
+
33
+ RSpec owns acceptance and is included in the default offline `bundle exec rake`
34
+ gate. Callable execution/grading helpers and subprocess-only preloads live in
35
+ `spec/support/harness`; locked JSON manifests, independent gold and SHA-256 files
36
+ live in `spec/fixtures/harness`. Helpers are explicitly required, not recursively
37
+ loaded into the test process: the provider preloads must only run in isolated
38
+ children. There is no separate script-suite CLI. The package example builds and
39
+ executes the installed gem, not a mocked packaging check.
40
+
41
+ The package lane copies current tracked and nonignored untracked working files
42
+ into a temporary checkout, records every copied SHA-256, builds the actual gem,
43
+ installs it using `--local --ignore-dependencies` into a temporary GEM_HOME, and
44
+ runs that installation's **actual pwn-ai executable → CLI → Loop → provider →
45
+ Dispatch → shell**. It never stages the source checkout or modifies its installed
46
+ gem. Dependencies are read from existing gem paths; this is **not** proof of a
47
+ clean dependency installation. Source edits concurrent with snapshot creation
48
+ are represented by the copied-byte ledger, not an atomic Git revision claim.
49
+
50
+ Each subprocess gets an isolated temporary HOME and cleared environment. The
51
+ configuration loader and provider methods are replaced in a test-only preload;
52
+ transparent observers forward to real Dispatch, Learning and Swarm methods.
53
+ Fault stimuli supply public Loop runtime/steering options without replacing its
54
+ implementation. Loop, Dispatch, the verifier and tools remain real. The commands are local
55
+ file operations. HTTP/TCP provider access is rejected, and no credentials are
56
+ passed. This is not a general OS-level sandbox for arbitrary untrusted fixtures.
57
+ The preload is never selected by production configuration.
58
+
59
+ The fixed public tasks cover exact-file output, structured extraction, safe Ruby
60
+ coding and a nonexecuted parameterized-SQL toy classification. Grading reads
61
+ actual artifact bytes against authored literal expectations, separately from
62
+ provider text. An intentionally wrong artifact with a success claim must remain
63
+ FAIL with CLI exit 1 and `false_success: false`. The arbiter checks the explicit
64
+ `containing exactly ... followed by newline` literal against the observed digest;
65
+ this deliberately narrow grammar is not a general natural-language content judge.
66
+ The CLI now derives its exit code from the persisted outcome, not answer prose.
67
+ A detected false success is a defect and fails the evaluator itself. Fixture
68
+ outcomes are not evidence of live model competence.
69
+
70
+ `runtime_faults.rb` retains the older, explicitly labeled component fixtures.
71
+ `cli_faults.rb` adds actual executable → Loop → runtime → real shell fault lanes:
72
+
73
+ - **Compaction plus additive steering:** real writes/reads generate long context,
74
+ a small runtime window forces EngagementMemory, a public Steering submission
75
+ adds a second deliverable, and independent checks require both actual files.
76
+ - **Provider retry after a successful effect:** the fake provider raises EOFError,
77
+ the real retry path recovers, and the effect log must still contain one write.
78
+ - **Cancel during a blocked provider:** after one real write, Steering cancels
79
+ the sleeping provider; the runtime must be cancelled, no final claim released,
80
+ and the CLI nonzero. This is not a native-terminal Ctrl+C test.
81
+ - **Checkpoint restart:** one real mutation times out, the process exits abruptly,
82
+ then a new CLI process restores the same request and refuses duplicate replay.
83
+ - **Hard resource cap and restart:** after at most 35 seconds of bounded executable
84
+ startup, an observed provider-fault readiness marker arms a four-second hard
85
+ process-group cap. The supervisor records INCOMPLETE, restores the real checkpoint
86
+ and proves that the uncertain mutation is not repeated. Missing readiness cannot
87
+ pass this lane. This separates cold gem activation from the injected blocked
88
+ provider: killing a still-booting executable proves neither effect nor recovery.
89
+
90
+ The fault preload supplies public request/session/runtime/steering objects.
91
+ These are real CLI integration tests with injected controls; the separate
92
+ `session_acceptance.rb` exercises public session selection and replacement approval
93
+ without injected runtime objects. They cover finite stimuli, not every provider HTTP
94
+ retry implementation. Package acceptance runs these same lanes against installed
95
+ gem code and checks deployment fingerprints. No live provider is contacted.
96
+
97
+ The additional baseline `timeout-replay` task executes one append before timeout,
98
+ refuses the second mutation, and must exit nonzero. The evaluator now reports
99
+ INCOMPLETE when a real observed Dispatch timeout prevents success; it does not
100
+ mistake an incomplete task for model-quality failure or hide it from results.
101
+
102
+ ## Request-lifetime integration boundary
103
+
104
+ RequestRuntime persists tool start/end events and uncertain mutation fingerprints
105
+ before releasing results. An interrupted write is not automatically replayed,
106
+ including after checkpoint restore. Changing the action ID or timeout does not
107
+ change that fingerprint. This is conservative uncertainty handling, not proof of
108
+ remote exactly-once execution; spelling changes can still change the fingerprint.
109
+ Explicit caller timeouts are not increased by Dispatch on runtime-owned requests.
110
+ Legacy standalone Dispatch budgeting remains separate. Runtime-owned inference
111
+ has one total provider/profile attempt allowance; all six provider adapters use
112
+ private configuration snapshots and one transport attempt under that owner.
113
+
114
+ Runtime-bound OpenGoal files are session/request-keyed, private atomic JSON, with
115
+ a session pointer for bare `continue` in the same session. Legacy global records
116
+ are read only for their matching session. Checkpoint generations reject stale
117
+ writers. Loop also holds an exclusive per-session execution flock for the entire
118
+ request, acquired before runtime loading/checkpointing and released on return,
119
+ exception or process death. This is separate from Sessions' shared cleanup lease;
120
+ both apply to real CLI/TUI requests. Distinct sessions remain independent. The
121
+ session execution lock serializes OpenGoal pointer updates on production Loop
122
+ paths; individual atomic file writes are not a general multi-file transaction.
123
+ A new one-shot CLI session is not an automatic cross-session resume.
124
+
125
+ Explicit operator entry points:
126
+
127
+ ```sh
128
+ pwn-ai --resume-session EXACT_SESSION_ID
129
+ pwn-ai --ai 'New objective' --verification-contract /path/to/authored.json
130
+ pwn-ai --replace-session EXACT_SESSION_ID --ai 'Replacement objective' \
131
+ --verification-contract /path/to/replacement.json --approve-replacement
132
+ ```
133
+
134
+ `--resume-session` rejects a replacement prompt/contract, selects the saved request
135
+ identity, and preserves its original objective, permissions and authored checks.
136
+ The existing TUI Sessions workspace and `/sessions resume ID` select exact stable
137
+ session IDs; a subsequent `continue` uses the same core restore path. A replacement
138
+ requires explicit approval, creates a **new** request identity and contract, and
139
+ retains the old checkpoint unchanged. It never edits the immutable original
140
+ objective or weakens an existing request's contract. `--resume RUN_ID` still means
141
+ TaskDAG resume and is not repurposed. Bare steering `replace:` remains an explicit
142
+ operator instruction; an existing authored contract is not replaced by steering.
143
+
144
+ Nested Loop turns now bind their own runtime artifact contract. Completed child
145
+ host receipts cross the enclosing Loop boundary only for a parent-required exact
146
+ path, matching child turn, current stat/readback/SHA-256 and a child started during
147
+ the parent request. Parent receipts retain child request/turn/action provenance.
148
+ The parent reruns its own literal/authored integration checks; child prose is never
149
+ proof. `swarm_acceptance.rb` executes agent_spawn → agent_ask → nested Loop and
150
+ real shell operations for correct, lying, preexisting/stale, missing and later
151
+ tampered artifacts. Negative task outcomes remain blocked/nonzero; PASS there
152
+ means the rejection invariant held, not that the requested work succeeded.
153
+
154
+ For nonreasoning OpenAI requests, explicit or configured temperature `0` is now
155
+ preserved in the actual serialized HTTP payload. The adapter regression intercepts
156
+ RestClient at the transport boundary rather than checking declared settings alone.
157
+ This is offline payload evidence, not a paired live reference-provider comparison.
158
+
159
+ Host-supplied verification contracts and tool permissions survive checkpoint
160
+ reload. Dispatch checks runtime permissions in addition to existing boundaries.
161
+ Additive steering verification binds the ordered original/amendment transcript,
162
+ not merely the last user line. Verifier events include original/effective request
163
+ and amendment digests, and report requirement character spans. An explicit
164
+ `answer` check compares actual final text and, when supplied, quoted/cited source
165
+ files through the bounded in-root verifier. No assistant PASS string is evidence.
166
+ These authored checks do not imply arbitrary research-answer semantic coverage.
167
+
168
+ These fixtures do not by themselves certify universal retry ownership, every
169
+ provider configuration, general semantic verification or scheduler correctness.
170
+ Those production requirements need their own acceptance evidence. An executable
171
+ arm which exposes a production defect remains FAIL and fails the aggregate
172
+ acceptance; actual correct artifact bytes do not override a nonzero CLI exit.
173
+
174
+ Results preserve stdout, stderr, exit/signal status, observed provider calls,
175
+ fingerprints, artifact hashes, wall latency, fixture-marked repeated effects and
176
+ interventions. Distinct effect IDs are distinct writes, not duplication: only
177
+ repeated observed IDs contribute to repeated_effects. Tool attempts alone do not.
178
+ Unknown usage/cost and uninstrumented all-learning activity remain null. The
179
+ parent supervisor kills the process group on wall cap and records INCOMPLETE,
180
+ never success. A generic child error is not silently converted into a pass.
181
+
182
+ ## Recorded offline integration acceptance
183
+
184
+ Final integration artifacts are retained under `/tmp/pwn-harness-final-acceptance`.
185
+ These are local execution results, not live provider scores:
186
+
187
+ - `HOME=/tmp/pwn-harness-final-acceptance/home bundle exec rake`:
188
+ **4369 examples, 0 failures**, then **1150 files, no RuboCop offenses**, exit 0
189
+ (`rake-post-package-fix.log`). The process was awaited through actual exit.
190
+ - The focused Loop/runtime/Dispatch/Swarm/verifier/CLI/OpenAI/console/evaluator
191
+ suite: **423 examples, 0 failures**, exit 0 (`focused-post-package-fix.log`).
192
+ - `HOME=/tmp/pwn-harness-final-acceptance/home bundle exec rubocop`:
193
+ **1150 files, no offenses**, exit 0 (`rubocop-post-package-fix.log`).
194
+ - Historical offline evaluator run (before the RSpec layout migration):
195
+ exit 0. Four baseline tasks PASS; artifact-lie remains FAIL/exit1, and
196
+ timeout-replay remains INCOMPLETE/exit1 with no repeated effect. All five
197
+ executable fault acceptance lanes, eight frozen task/arm trials, five Swarm
198
+ evidence acceptance lanes and two public session-selection lanes passed.
199
+ Negative task outcomes were not relabeled successful work.
200
+ - Reusing `offline-final/experiments` as the frozen source via
201
+ `HarnessExperiments.run(output: '/tmp/pwn-harness-final-acceptance/frozen-replay',
202
+ frozen: '/tmp/pwn-harness-final-acceptance/offline-final/experiments')` returned
203
+ exit 0 with equal snapshot digest, schedule, input hashes and graded outcomes.
204
+ - Historical packaged acceptance output: `package-verified` beneath the artifact root above.
205
+ Current entry point: `bundle exec rspec spec/integration/harness_package_spec.rb`.
206
+ Its `acceptance.json` is the authority for installed execution and source-byte
207
+ equality, not the earlier source-only counts above.
208
+
209
+ The initial real evaluator's two Swarm failures, temperature-zero red regression,
210
+ cold-gem cap-before-readiness package failure and corrected lint failures remain
211
+ in the acceptance ledger rather than being hidden. Natural-language truth requires
212
+ authored checks; all live/private model
213
+ comparison, quality and superiority claims remain **NOT_RUN**.
214
+
215
+ ## Live comparison protocol — NOT_RUN
216
+
217
+ `manifest.json` and `adapters.json` have SHA-256 lock files. The runner rejects
218
+ manifest drift. Adapter metadata pins the inspected reference source revision
219
+ `601b550b4b5addecc0b6dacf9a472a54dd8c6799` and relevant source hashes. It documents
220
+ real inspected entry points and constructor fields rather than invented CLI
221
+ flags. The reference API is `run_agent.AIAgent(...).run_conversation(...)`.
222
+ No reference agent or live provider is launched by the offline evaluator.
223
+ `reference_adapter.py` is a separate optional Python-library boundary. It checks
224
+ the source revision/hashes and real constructor signature, then invokes the
225
+ public API with explicit provider, endpoint, model, reasoning, temperature via
226
+ `request_overrides`, toolsets, token/iteration/wall budgets, fresh history, and
227
+ disabled context files/memory/background review. It requires `--approve-live`,
228
+ an explicitly supplied key, temporary HOME/cwd, a locked same-model parity
229
+ manifest, reviewed sandbox/tool parity, and an operator-attested provider-side
230
+ hard cost cap. Unknown or unequal provider/model/revision values reject execution.
231
+ Raw returned data stays UNVERIFIED until an independent artifact checker grades
232
+ it. Aggregate unobserved usage/cost remain null rather than fabricated zeros.
233
+
234
+ The optional `spec/integration/harness_live_spec.rb` is tagged `:harness_live`
235
+ and excluded unless `PWN_TEST_HARNESS_LIVE=1`. It never adds pending examples to
236
+ the default gate. Explicit opt-in also requires `PWN_HARNESS_REFERENCE_SOURCE`,
237
+ `PWN_HARNESS_LIVE_MANIFEST`, `PWN_HARNESS_PROMPT_FILE`, and `PWN_EVAL_API_KEY`;
238
+ missing prerequisites fail rather than skip. The adapter's approval, locked
239
+ parity, pinned-source, temporary HOME/cwd and cost-cap checks remain mandatory.
240
+ This optional example observes only the reference API response; it does not
241
+ establish a paired comparison or model-quality gain. No live lane was run for
242
+ the layout migration.
243
+
244
+ Safe static inspection (does not import the agent or read credentials):
245
+
246
+ ```sh
247
+ python3 spec/support/harness/reference_adapter.py --inspect-source /path/to/pinned/reference/source
248
+ ```
249
+
250
+ `live_comparison.json` deliberately has unselected provider/model/revision fields
251
+ and unapproved budget/parity. It is not runnable until an operator supplies and
252
+ locks those choices. Static API validation is not live provider validation or
253
+ proof that both providers honor all parity settings on the wire.
254
+
255
+ Before any comparison, fill and freeze the **same immutable model/version,
256
+ reasoning setting, temperature, tools/permissions, task inputs, environment,
257
+ iteration/token/wall/cost budget and learned snapshot** for both adapters. These
258
+ fields are deliberately unselected, not guessed. Audit equivalent sandbox and
259
+ sidecar behavior, instrument all provider calls and billable usage, and enforce
260
+ operator-approved cost caps outside both agents. Missing parity means NOT_RUN.
261
+
262
+ Use paired repeated seeds and randomized arm order. Freeze independently authored
263
+ heldout tasks before tuning; these public scripted regression examples are not a
264
+ private holdout. Keep raw transcripts and independent artifact checks. Report
265
+ success, false-success, repeated effects, interventions, latency and observed
266
+ usage/cost, including failures and caps; do not drop incomplete runs. Confidence
267
+ intervals and claims of improvement require actual paired live trials.
268
+
269
+ ## Frozen split execution and scoped ablations
270
+
271
+ `experiments.rb` executes separate train, validation and heldout task identities.
272
+ `experiment_tasks.json` and separately authored `experiment_gold.json` are locked
273
+ before execution. The checker reads actual bytes; gold is not included in provider
274
+ fixtures or task workspaces. Duplicate task IDs, prompts or input bytes across
275
+ splits are rejected. This checks explicit leakage, not semantic dataset similarity.
276
+ The supplied public corpus and scripted provider are infrastructure regressions,
277
+ **not a secret holdout, a live intelligence benchmark, or evidence of learning gains**.
278
+
279
+ Training uses the real CLI and real Policy paths. After host byte checks succeed,
280
+ the real Learning.note_outcome API records a labeled synthetic result without an
281
+ invented reward score. The entire isolated training state is copied and frozen
282
+ with a per-file hash ledger. Every evaluation trial clones it into a fresh HOME;
283
+ no heldout writes flow back into the source snapshot or another trial. The runner
284
+ checks the source ledger again after evaluation. Per-trial policy episode counts
285
+ exclude episodes inherited from the snapshot. A seeded randomized schedule is
286
+ recorded verbatim; reusing a frozen snapshot preserves the input state, not elapsed
287
+ times or model randomness.
288
+
289
+ All offered arms execute real PWN paths:
290
+
291
+ | Arm | Executed path |
292
+ | --- | --- |
293
+ | `single-cold` | CLI → Loop, empty HOME, policy disabled |
294
+ | `single-snapshot` | CLI → Loop → real learning retrieval, frozen training stores, policy disabled |
295
+ | `single-policy` | CLI → Loop → Policy begin/observe/finish, same snapshot, per-trial writes |
296
+ | `swarm-snapshot` | CLI → Dispatch agent_spawn/agent_ask → Swarm.ask → nested Loop → real shell; parent readback |
297
+
298
+ Cold HOME is **not** an all-learning-off switch. The fixture disables automatic
299
+ introspection/extrospection in every arm, but ordinary metrics and retrieval are
300
+ real. No unimplemented all-learning-off/on arm is offered. Swarm provider calls,
301
+ learning-context invocations and policy episodes are observed rather than inferred.
302
+ These unequal fixture call counts are wiring evidence, not fair budget-normalized
303
+ model superiority. Model quality remains NOT_RUN.
304
+
305
+ For a diagnostic replay of a previously retained frozen training state, require
306
+ `spec/support/harness/experiments` and call `HarnessExperiments.run(output: ...,
307
+ frozen: ...)` from Ruby. Use a fresh `/tmp` output directory. This returns raw
308
+ observations; the integration specs own acceptance, not a helper's exit status.
309
+ Custom locked manifests and gold use the `manifest:` and `gold:` options. They
310
+ are trusted local executable fixtures, not untrusted code; the HTTP guard is not
311
+ an OS sandbox for arbitrary shell commands. Keep private benchmark gold outside
312
+ any live agent sandbox. Do not tune on heldout outcomes.
@@ -9,6 +9,75 @@ so it doesn't repeat it**.
9
9
 
10
10
  ## Two ways to run it
11
11
 
12
+ ### Local operator workspace
13
+
14
+ The curses console exposes these local views through the command menu:
15
+
16
+ * `/sessions` (or `/sessions picker`) opens a searchable picker. Type to filter
17
+ titles or stable IDs, use Up/Down to select, Enter to inspect local activity,
18
+ held activity lease, summary provenance, referenced-by paths and any recorded
19
+ unfinished goal, then `r` to resume that exact ID. Duplicate titles are never
20
+ guessed. Tab refreshes while preserving the selected ID. Esc restores the draft.
21
+ * `/readiness` previews the configured four-role routes, credential **presence**
22
+ (never credential values), local required tool paths, workspace and limits.
23
+ `w` toggles optional Git-worktree execution, `+`/`-` sets 1–10 repair rounds,
24
+ `t` cycles the bounded per-check timeout and `r` cycles the per-role timeout.
25
+ These console-lifetime options
26
+ feed the next mission without modifying the objective or encrypted config.
27
+ Navigation never logs in, installs dependencies or calls a provider. Readiness
28
+ is not a connectivity test; task-specific dependencies are not inferred.
29
+ * `/evidence` lists retained mission records and current console mission snapshots.
30
+ Enter inspects the original requirements, candidate revision, artifacts,
31
+ executed checks, observations, blockers and recorded outcome. Tab reloads
32
+ persisted records. A canceled mission retains its evidence. On a worktree
33
+ result, `d` opens the checksum-verified changed/deleted-file patch; `a` then
34
+ requires typing `APPLY <tested-revision>`. The backend rechecks source,
35
+ candidate, patch and tested revision before applying; it never stages or commits.
36
+ `x` requests removal of an owned checkout after typing `DISCARD <revision>`
37
+ (or its displayed ownership token before a candidate exists). A changed checkout
38
+ is refused with recovery guidance; patch and evidence are retained.
39
+ Running phase checkpoints are atomically persisted before publication, including
40
+ actual check records, uncovered requirements and repair reasons.
41
+ Worktrees isolate edits, **not** arbitrary code execution or network access.
42
+ * `/sessions cleanup` previews the same provenance/dependency policy as scheduled
43
+ cleanup. Inspect the exact IDs, paths, fingerprints, retention reasons and
44
+ uncertainty. `a`, then Enter confirms only the displayed candidate set.
45
+ Newly discovered or changed records are excluded. Apply rechecks activity,
46
+ references and source bytes under locks, backs up candidate bytes, and reports
47
+ actual deletions and skipped records. No deletion happens during preview.
48
+ * `/learning inbox` lists learning outcomes and retained skill proposals.
49
+ Enter inspects the exact procedure, source fields and fingerprints; `e` inspects
50
+ retained execution evidence. `a` approves, `x` rejects and `u` rolls back, each
51
+ requiring Enter confirmation. For skills, approval appends the proposed procedure
52
+ to an existing non-generated skill; rollback restores its backup only if the
53
+ approved file is unchanged. Source changes, absent or failing independent proof,
54
+ conflicted outcomes, generated skills and legacy proposals without procedure
55
+ payload remain blocked. Creation of a new skill is not enabled by this approval
56
+ API. Outcome decisions remain audit records, never fabricated training labels.
57
+ SkillReview evidence references use `learning_id` and `learning_sha256` for an
58
+ immutable learning row with retained full-session evidence and passing host
59
+ Verification checks. A model's `fixture_passed` assertion alone is insufficient,
60
+ including in `auto-safe` mode. No provider is called during review.
61
+ * `/guide` is a short dismissible guide. Its numbered examples insert editable
62
+ draft text without executing it; existing drafts are retained. `x` records
63
+ dismissal in a private local preference marker; Esc closes without writing it.
64
+
65
+ All these overlays support scrollback with PgUp/PgDn/Home/End and preserve the
66
+ mission draft. Requested cancellation and pause are not reported as completion:
67
+ pause becomes observed only at a coordinator boundary. Local list/readiness scans
68
+ run on a coalesced event worker; stale results cannot replace a newly opened view.
69
+ Ordinary curses and legacy steerable requests and missions share canonical-workspace
70
+ admission locks; a second writer receives
71
+ an actionable busy error. Other applications and unupgraded clients do not honor
72
+ these advisory locks.
73
+
74
+ For Ruby callers, `PWN::AI::Agent::Swarm.solve(request: objective, workspace: root,
75
+ worktree: true, rounds: 3, verification_timeout: 60, role_timeout: 300)` uses the
76
+ same mission backend. Timeouts are 1–600 seconds; rounds are 1–10. Worktree mode
77
+ requires a clean Git root (including ignored and untracked files). It does not
78
+ stage, commit or automatically apply the candidate. Preserve `request` verbatim;
79
+ options are separate from the objective.
80
+
12
81
  ```text
13
82
  # 1. Interactive curses console (inside the pwn REPL)
14
83
  pwn[CURRENT_VERSION]:001 >>> pwn-ai
@@ -164,6 +233,7 @@ changes only default values, not the configuration schema; no migration is neede
164
233
  | PgUp/PgDn | Scroll the timeline; new output does not pull a scrolled viewport to the bottom |
165
234
  | `/model`, `/sessions resume ID` | Show model settings / change the next request's model or session while idle |
166
235
  | `/system-role` | Open the active engine's multiline SYSTEM ROLE CONTENT editor while idle; Ctrl+S saves, Esc cancels |
236
+ | `/vault edit [key ...]` | Privately edit encrypted configuration or one nested mapping scope; Ctrl+S saves, Esc/Ctrl+C cancels |
167
237
  | `/steer INSTRUCTION` | Redirect the active request at a safe boundary |
168
238
  | `/input TEXT` | Send one line to an ordinary tool stdin prompt; not retained in input recall |
169
239
  | Ctrl+C | Cancel the active request cooperatively, or clear an idle draft |
@@ -197,6 +267,58 @@ the new role immediately, and a new session loads it from the vault. Request or
197
267
  swarm activity blocks role changes. No schema migration is required. The legacy
198
268
  non-curses interface reports that this editor requires an interactive terminal.
199
269
 
270
+ ### Private vault editor
271
+
272
+ `/vault edit` opens the complete **PRIVATE YAML** document; `/vault edit ai
273
+ anthropic` opens that nested mapping, and `/vault edit ai anthropic model`
274
+ opens a scalar. Tokens identify one nested key path, never filenames or multiple
275
+ disjoint scopes. The target and decryptor are the same `driver_opts.pwn_env_path`
276
+ and `driver_opts.pwn_dec_path` used by `pwn-vault`, defaulting to
277
+ `~/.pwn/pwn.yaml` and its existing `.decryptor` sidecar. Unknown paths are rejected; edit the parent mapping
278
+ to add a new key. Arrays are edited as a whole, not by numeric path indices.
279
+ Existing non-null types must be retained; an existing null may remain null or
280
+ be replaced by a YAML value. The root must retain an `ai` mapping, a supported
281
+ `ai.active`, and its engine mapping. Mapping keys may be added or removed within
282
+ the selected scope. Duplicate keys, aliases, object tags and multiple documents
283
+ are rejected. Nesting is limited to 100 mappings/sequences, including the merged
284
+ full document and decryptor parsing, before recursive YAML conversion. Invalid
285
+ input leaves the encrypted file intact. YAML formatting/comments are normalized on save.
286
+
287
+ Enter inserts a newline, arrows move, Home/End move to the document ends and
288
+ Ctrl+U clears. **Ctrl+S** explicitly saves encrypted; **Esc/Ctrl+C** discards
289
+ changes, and Ctrl+D leaves without saving. Save errors remain redacted inside
290
+ the modal so the text can be corrected. No editor contents enter SESSION,
291
+ history, completion suggestions, logs or provider requests. Completion suggests
292
+ only shipped schema key names, never reads/decrypts the vault, and never offers
293
+ configuration values; existing custom keys can be typed explicitly.
294
+
295
+ Plaintext is decrypted/edited in memory. Only encrypted bytes enter the private
296
+ temporary file, which is atomically renamed over the vault using the same
297
+ AES-256-CBC format/key/IV as `pwn-vault`; the decryptor is not regenerated.
298
+ Save re-reads the current document, preserves changes outside the selected scope,
299
+ and refuses a changed selected scope (the whole document for unscoped edits).
300
+ Editor instances serialize saves with a directory lock; a final ciphertext check
301
+ also detects intervening changes from other writers. Configuration is updated
302
+ locally without running authentication or `Config.refresh_env`. Editing is
303
+ blocked while a request/swarm is active. The legacy non-curses interface explains
304
+ that an interactive curses console is required and makes no changes.
305
+
306
+ ### Bounded nested data
307
+
308
+ JSON-bound tool results, schema copies/normalization, MCP arguments, Swarm key
309
+ conversion and persistence redaction reject cyclic or over-deep containers with
310
+ `JSON::NestingError`, using the serializer's 100-container default. Credential
311
+ collection for result redaction walks the environment by identity without Ruby
312
+ recursion, so repeated or cyclic mappings do not prevent credential replacement.
313
+ Redaction validates even sensitive-field containers before inspecting them;
314
+ invalid input fails closed rather than returning a raw value. Valid shared
315
+ branches remain independent output copies and inputs are not modified.
316
+
317
+ Local XML ingestion preflights element depth (maximum 100) before recursive
318
+ REXML XPath/formatting, and rejects excessive nesting with `ArgumentError`
319
+ before database or embedding work. These bounds cover the named preprocessing
320
+ paths, not arbitrary Ruby/plugin callbacks or all possible VM stack failures.
321
+
200
322
  ### Model and reasoning selection
201
323
 
202
324
  `/model` (also `show` or `status`) displays the current selection. Existing
@@ -149,7 +149,7 @@ module PWN
149
149
  publish(File.join(result[:archive_dir], 'ledger.json'), JSON.pretty_generate(result))
150
150
  end
151
151
 
152
- def self.run(dry_run: true, current_session_id: nil, proc_root: '/proc')
152
+ def self.run(dry_run: true, current_session_id: nil, proc_root: '/proc', approved_candidates: nil)
153
153
  raise 'Session cleanup requires the updated Sessions activity API; load the updated source or deploy it before running this helper' unless PWN::Sessions.respond_to?(:activate)
154
154
 
155
155
  root = PWN::Sessions.sessions_dir
@@ -177,6 +177,11 @@ module PWN
177
177
  end
178
178
  expected = snapshot(path)
179
179
  record[:files] = expected
180
+ if approved_candidates && approved_candidates[id] != expected
181
+ record[:skip] = :not_approved_or_changed
182
+ result[:skipped][:not_approved_or_changed] += 1
183
+ next
184
+ end
180
185
  result[:unnamed] += 1
181
186
  if PWN::Sessions.active?(session_id: id)
182
187
  record[:skip] = :active
@@ -36,6 +36,7 @@ PWN::AI::Agent::Dispatch.call(opts)
36
36
  - `call`
37
37
  - `repair_name`
38
38
  - `tool_calls_from_text`
39
+ - `scheduling`
39
40
  - `effect`
40
41
  - `authors`
41
42
  - `help`
@@ -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