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.
- checksums.yaml +4 -4
- data/Gemfile +3 -3
- data/documentation/Configuration.md +2 -0
- data/documentation/General-PWN-Usage.md +68 -0
- data/documentation/Harness-Evaluation.md +312 -0
- data/documentation/pwn-ai-Agent.md +122 -0
- data/etc/default_cron/session_cleanup.rb +6 -1
- data/etc/default_skills/pwn/ai/agent/dispatch/SKILL.md +1 -0
- data/etc/default_skills/pwn/ai/agent/open_goal/SKILL.md +1 -0
- data/etc/default_skills/pwn/ai/agent/skill_review/SKILL.md +2 -0
- data/etc/default_skills/pwn/ai/agent/solve/SKILL.md +4 -2
- data/etc/default_skills/pwn/ai/context_ingestion/SKILL.md +1 -1
- data/etc/default_skills/pwn/ai/http_retry/SKILL.md +1 -0
- data/etc/default_skills/pwn/plugins/repl/ai_console/SKILL.md +1 -1
- data/etc/default_skills/pwn/plugins/repl/mesh/SKILL.md +4 -0
- data/etc/default_skills/pwn/plugins/repl/mesh/references/urls.md +3 -0
- data/etc/default_skills/pwn/redaction/SKILL.md +1 -1
- data/etc/default_skills/pwn/runtime_fingerprint/SKILL.md +47 -0
- data/lib/pwn/ai/agent/dispatch.rb +50 -6
- data/lib/pwn/ai/agent/loop.rb +192 -115
- data/lib/pwn/ai/agent/open_goal.rb +43 -1
- data/lib/pwn/ai/agent/registry.rb +6 -1
- data/lib/pwn/ai/agent/request_runtime.rb +508 -17
- data/lib/pwn/ai/agent/result.rb +23 -6
- data/lib/pwn/ai/agent/reward.rb +4 -1
- data/lib/pwn/ai/agent/skill_review.rb +148 -1
- data/lib/pwn/ai/agent/solve.rb +420 -39
- data/lib/pwn/ai/agent/swarm.rb +11 -2
- data/lib/pwn/ai/agent/tools/shell.rb +23 -0
- data/lib/pwn/ai/agent/turn_finalizer.rb +37 -6
- data/lib/pwn/ai/agent/verification.rb +42 -5
- data/lib/pwn/ai/anthropic.rb +16 -5
- data/lib/pwn/ai/cli.rb +48 -4
- data/lib/pwn/ai/context.rb +1 -1
- data/lib/pwn/ai/context_ingestion.rb +16 -1
- data/lib/pwn/ai/gemini.rb +9 -3
- data/lib/pwn/ai/grok.rb +10 -5
- data/lib/pwn/ai/http_retry.rb +14 -0
- data/lib/pwn/ai/mcp/combo_nation.rb +5 -2
- data/lib/pwn/ai/mcp.rb +5 -2
- data/lib/pwn/ai/ollama.rb +7 -3
- data/lib/pwn/ai/open_ai.rb +11 -11
- data/lib/pwn/ai/open_web_ui.rb +7 -3
- data/lib/pwn/config.rb +6 -2
- data/lib/pwn/migrate.rb +5 -1
- data/lib/pwn/plugins/repl/ai.rb +15 -1
- data/lib/pwn/plugins/repl/ai_console.rb +843 -19
- data/lib/pwn/plugins/repl/ai_console_commands.rb +23 -5
- data/lib/pwn/plugins/repl/ai_swarm.rb +65 -5
- data/lib/pwn/plugins/repl/mesh.rb +894 -83
- data/lib/pwn/plugins/repl.rb +2 -0
- data/lib/pwn/redaction.rb +29 -5
- data/lib/pwn/runtime_fingerprint.rb +84 -0
- data/lib/pwn/version.rb +1 -1
- data/lib/pwn.rb +1 -0
- data/spec/fixtures/harness/adapters.json +62 -0
- data/spec/fixtures/harness/adapters.json.sha256 +1 -0
- data/spec/fixtures/harness/experiment_gold.json +9 -0
- data/spec/fixtures/harness/experiment_gold.json.sha256 +1 -0
- data/spec/fixtures/harness/experiment_tasks.json +12 -0
- data/spec/fixtures/harness/experiment_tasks.json.sha256 +1 -0
- data/spec/fixtures/harness/live_comparison.json +21 -0
- data/spec/fixtures/harness/live_comparison.json.sha256 +1 -0
- data/spec/fixtures/harness/manifest.json +290 -0
- data/spec/fixtures/harness/manifest.json.sha256 +1 -0
- data/spec/integration/harness_evaluation_spec.rb +171 -0
- data/spec/integration/harness_layout_spec.rb +15 -0
- data/spec/integration/harness_live_spec.rb +26 -0
- data/spec/integration/harness_package_spec.rb +38 -0
- data/spec/integration/native_ruby_helpers_spec.rb +4 -2
- data/spec/integration/reinforced_feedback_loop_spec.rb +2 -2
- data/spec/lib/pwn/ai/agent/dispatch_spec.rb +47 -3
- data/spec/lib/pwn/ai/agent/loop_spec.rb +479 -17
- data/spec/lib/pwn/ai/agent/open_goal_spec.rb +17 -0
- data/spec/lib/pwn/ai/agent/registry_spec.rb +10 -0
- data/spec/lib/pwn/ai/agent/request_runtime_spec.rb +232 -0
- data/spec/lib/pwn/ai/agent/result_spec.rb +33 -0
- data/spec/lib/pwn/ai/agent/signal_hygiene_spec.rb +1 -1
- data/spec/lib/pwn/ai/agent/skill_review_spec.rb +17 -5
- data/spec/lib/pwn/ai/agent/solve_spec.rb +390 -7
- data/spec/lib/pwn/ai/agent/swarm_spec.rb +19 -0
- data/spec/lib/pwn/ai/agent/tools/ruby_eval_spec.rb +2 -2
- data/spec/lib/pwn/ai/agent/turn_finalizer_spec.rb +20 -0
- data/spec/lib/pwn/ai/agent/verification_spec.rb +12 -0
- data/spec/lib/pwn/ai/anthropic_spec.rb +10 -0
- data/spec/lib/pwn/ai/cli_spec.rb +40 -0
- data/spec/lib/pwn/ai/context_ingestion_spec.rb +12 -0
- data/spec/lib/pwn/ai/mcp/combo_nation_spec.rb +10 -0
- data/spec/lib/pwn/ai/mcp_spec.rb +10 -0
- data/spec/lib/pwn/ai/open_ai_spec.rb +13 -0
- data/spec/lib/pwn/migrate_spec.rb +37 -4
- data/spec/lib/pwn/plugins/repl/ai_console_completion_wrap_spec.rb +110 -0
- data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +307 -3
- data/spec/lib/pwn/plugins/repl/ai_swarm_spec.rb +29 -0
- data/spec/lib/pwn/plugins/repl/ai_vault_edit_spec.rb +221 -0
- data/spec/lib/pwn/plugins/repl_mesh_channel_identity_spec.rb +134 -0
- data/spec/lib/pwn/plugins/repl_mesh_dispatch_spec.rb +2 -0
- data/spec/lib/pwn/plugins/repl_mesh_dm_transport_spec.rb +2 -1
- data/spec/lib/pwn/plugins/repl_mesh_multi_rx_spec.rb +315 -0
- data/spec/lib/pwn/plugins/repl_mesh_origin_gps_spec.rb +120 -0
- data/spec/lib/pwn/plugins/repl_mesh_rx_errors_spec.rb +325 -0
- data/spec/lib/pwn/plugins/repl_mesh_scroll_spec.rb +49 -0
- data/spec/lib/pwn/plugins/repl_mesh_transport_label_spec.rb +43 -15
- data/spec/lib/pwn/plugins/repl_spec.rb +6 -5
- data/spec/lib/pwn/redaction_spec.rb +17 -0
- data/spec/lib/pwn/runtime_fingerprint_spec.rb +53 -0
- data/spec/lib/pwn/session_cleanup_default_spec.rb +1 -1
- data/spec/spec_helper.rb +3 -1
- data/spec/support/harness/cli_faults.rb +107 -0
- data/spec/support/harness/evaluate.rb +176 -0
- data/spec/support/harness/experiment_preload.rb +20 -0
- data/spec/support/harness/experiments.rb +137 -0
- data/spec/support/harness/fake_provider.rb +80 -0
- data/spec/support/harness/fault_preload.rb +44 -0
- data/spec/support/harness/package_acceptance.rb +77 -0
- data/spec/support/harness/record_training.rb +18 -0
- data/spec/support/harness/reference_adapter.py +128 -0
- data/spec/support/harness/runtime_faults.rb +97 -0
- data/spec/support/harness/session_acceptance.rb +64 -0
- data/spec/support/harness/swarm_acceptance.rb +61 -0
- data/third_party/pwn_rdoc.jsonl +120 -1
- metadata +46 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4a5ecce60b421a59e80f691710dd5ae25cd79d083b59207b25baccf8fe8e5b35
|
|
4
|
+
data.tar.gz: 725ceb379d5436565b707e29d26e61e9cc10262b31e7cec537aec0e210d77fea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
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.
|
|
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
|