pwn 0.5.763 → 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 +11 -11
- data/documentation/Configuration.md +2 -0
- data/documentation/General-PWN-Usage.md +68 -0
- data/documentation/Harness-Evaluation.md +312 -0
- data/documentation/Installation.md +104 -0
- data/documentation/Reinforcement-Learning.md +80 -0
- data/documentation/pwn-REPL.md +74 -0
- data/documentation/pwn-ai-Agent.md +122 -0
- data/etc/default_cron/session_cleanup.rb +260 -0
- data/etc/default_skills/pwn/ai/agent/dispatch/SKILL.md +1 -0
- data/etc/default_skills/pwn/ai/agent/learning/SKILL.md +9 -2
- 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/SKILL.md +8 -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/etc/default_skills/pwn/sessions/SKILL.md +10 -0
- data/lib/pwn/ai/agent/curriculum.rb +9 -2
- data/lib/pwn/ai/agent/dispatch.rb +50 -6
- data/lib/pwn/ai/agent/learning.rb +365 -0
- data/lib/pwn/ai/agent/loop.rb +195 -114
- 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 +16 -2
- 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 +21 -5
- data/lib/pwn/ai/agent/tools/learning.rb +20 -3
- data/lib/pwn/ai/agent/tools/sessions.rb +2 -1
- 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 +17 -6
- data/lib/pwn/ai/cli.rb +52 -5
- 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 +11 -6
- 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 +12 -12
- data/lib/pwn/ai/open_web_ui.rb +7 -3
- data/lib/pwn/ai/router.rb +56 -12
- data/lib/pwn/config.rb +6 -2
- data/lib/pwn/cron.rb +35 -1
- data/lib/pwn/migrate.rb +9 -1
- data/lib/pwn/plugins/repl/ai.rb +191 -32
- data/lib/pwn/plugins/repl/ai_console.rb +915 -34
- 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 +955 -81
- data/lib/pwn/plugins/repl.rb +11 -1
- data/lib/pwn/redaction.rb +29 -5
- data/lib/pwn/runtime_fingerprint.rb +84 -0
- data/lib/pwn/sessions.rb +567 -22
- 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/learning_spec.rb +229 -0
- 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 +29 -1
- data/spec/lib/pwn/ai/agent/tools/learning_spec.rb +14 -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 +55 -1
- 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/ai/router_spec.rb +53 -0
- data/spec/lib/pwn/banner_spec.rb +1 -1
- data/spec/lib/pwn/cron_spec.rb +4 -3
- data/spec/lib/pwn/migrate_spec.rb +37 -4
- data/spec/lib/pwn/plugins/repl/ai_console_commands_spec.rb +9 -0
- data/spec/lib/pwn/plugins/repl/ai_console_completion_wrap_spec.rb +110 -0
- data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +558 -8
- data/spec/lib/pwn/plugins/repl/ai_result_chronology_spec.rb +210 -0
- data/spec/lib/pwn/plugins/repl/ai_spec.rb +209 -0
- 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 +264 -0
- data/spec/lib/pwn/plugins/repl_spec.rb +8 -5
- data/spec/lib/pwn/redaction_spec.rb +20 -2
- data/spec/lib/pwn/runtime_fingerprint_spec.rb +53 -0
- data/spec/lib/pwn/session_cleanup_default_spec.rb +72 -0
- data/spec/lib/pwn/session_cleanup_payload_spec.rb +593 -0
- data/spec/lib/pwn/sessions_spec.rb +344 -0
- 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 +169 -2
- metadata +67 -24
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,19 +50,19 @@ 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'
|
|
57
57
|
gem 'netaddr', '2.0.6'
|
|
58
|
-
gem 'net-ldap', '0.
|
|
58
|
+
gem 'net-ldap', '0.21.0'
|
|
59
59
|
gem 'net-openvpn', '0.8.7'
|
|
60
60
|
gem 'net-smtp', '0.5.2'
|
|
61
61
|
gem 'nexpose', '7.3.0'
|
|
62
62
|
gem 'nokogiri', '1.19.4'
|
|
63
63
|
gem 'nokogiri-diff', '0.3.0'
|
|
64
64
|
gem 'oily_png', '1.2.1'
|
|
65
|
-
gem 'open3', '0.
|
|
65
|
+
gem 'open3', '0.3.0'
|
|
66
66
|
gem 'os', '1.1.4'
|
|
67
67
|
gem 'ostruct', '0.6.3'
|
|
68
68
|
gem 'packetfu', '2.0.0'
|
|
@@ -73,15 +73,15 @@ gem 'pry', '0.16.0'
|
|
|
73
73
|
gem 'pry-doc', '1.7.0'
|
|
74
74
|
gem 'rake', '13.4.2'
|
|
75
75
|
gem 'rb-readline', '0.5.5'
|
|
76
|
-
gem 'rbvmomi2', '3.
|
|
76
|
+
gem 'rbvmomi2', '3.11.0'
|
|
77
77
|
gem 'rdoc', '7.0.4'
|
|
78
78
|
gem 'rest-client', '2.1.0'
|
|
79
79
|
gem 'rex', '2.0.13'
|
|
80
|
-
gem 'rmagick', '7.
|
|
80
|
+
gem 'rmagick', '7.2.0'
|
|
81
81
|
gem 'rqrcode', '3.2.0'
|
|
82
82
|
gem 'rspec', '3.13.2'
|
|
83
83
|
gem 'rtesseract', '3.1.4'
|
|
84
|
-
gem 'rubocop', '1.
|
|
84
|
+
gem 'rubocop', '1.92.0'
|
|
85
85
|
gem 'rubocop-rake', '0.7.1'
|
|
86
86
|
gem 'rubocop-rspec', '3.10.2'
|
|
87
87
|
gem 'ruby-audio', '1.6.1'
|
|
@@ -89,12 +89,12 @@ gem 'ruby-nmap', '1.0.3'
|
|
|
89
89
|
gem 'ruby-saml', '1.18.1'
|
|
90
90
|
gem 'rvm', '1.11.3.9'
|
|
91
91
|
gem 'savon', '2.17.5'
|
|
92
|
-
gem 'selenium-devtools', '0.
|
|
93
|
-
gem 'selenium-webdriver', '4.
|
|
94
|
-
gem 'slack-ruby-client', '3.
|
|
92
|
+
gem 'selenium-devtools', '0.155.0'
|
|
93
|
+
gem 'selenium-webdriver', '4.51.0'
|
|
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.
|
|
@@ -202,6 +202,110 @@ without a per-job crontab line. Live Policy files (`policy.json`,
|
|
|
202
202
|
`policy_traj.jsonl`) are created on first agent turn when `ai.agent.policy`
|
|
203
203
|
is on.
|
|
204
204
|
|
|
205
|
+
Schema `v8` seeds the enabled `session_cleanup` hygiene job, daily at 03:45
|
|
206
|
+
in the scheduler's local timezone. First launch also seeds it through
|
|
207
|
+
`PWN::Cron.install_defaults`. The bundled `etc/default_cron/session_cleanup.rb`
|
|
208
|
+
is installed to `~/.pwn/cron/session_cleanup.rb`; managed copies refresh on
|
|
209
|
+
subsequent launches/migrations, while unmarked custom helpers are preserved.
|
|
210
|
+
An existing job named `session_cleanup` keeps its ID, schedule, enabled state,
|
|
211
|
+
payload and native scheduler drop-ins. New jobs defer cleanup until their first
|
|
212
|
+
scheduled time, rather than deleting during initial catch-up.
|
|
213
|
+
|
|
214
|
+
Cleanup calls a session named only when its persisted metadata has
|
|
215
|
+
`title_source: summary`, a nonblank title and summary, and a positive integer
|
|
216
|
+
`summary_turns` cursor: the fields published after validated LLM title generation.
|
|
217
|
+
Provisional first-request titles, manual titles, persona labels, aliases and
|
|
218
|
+
historical embedded titles are not evidence of LLM summarization. Legacy records
|
|
219
|
+
without provenance are unsummarized with `missing_summary_provenance`; invalid or
|
|
220
|
+
incomplete metadata is unknown, never named. `/sessions list` and cleanup share
|
|
221
|
+
`title_source`, `is_summarized`, `classification` and `title_reason`. Classification
|
|
222
|
+
does not establish deletion safety: malformed records, symlinks and uncertain pin
|
|
223
|
+
stores remain protected. Age alone does not preserve an unsummarized session.
|
|
224
|
+
Version 5 backs up unreferenced unsummarized sessions before a guarded `Sessions.delete`
|
|
225
|
+
call that bypasses the ordinary age pin. Current sessions and every known
|
|
226
|
+
learning/mistake/memory reference remain protected. `/sessions list` exposes
|
|
227
|
+
`dependencies` as an array of files **referencing this session** (empty when
|
|
228
|
+
none), not files this session depends on. `dependency_errors` lists unknown,
|
|
229
|
+
malformed or unsafe stores; cleanup fails closed when it is nonempty.
|
|
230
|
+
The shared index follows defined structural fields, not ID-looking text.
|
|
231
|
+
Dependency-backed sessions remain visible and resumable, not archived away.
|
|
232
|
+
Vault/config/credential payloads and historical backups are not scanned.
|
|
233
|
+
Same-user interactive PWN
|
|
234
|
+
processes defer deletion because existing writers have no lifetime leases; this is a
|
|
235
|
+
conservative guard, not a cross-process session lease. Inspect without deleting:
|
|
236
|
+
`ruby ~/.pwn/cron/session_cleanup.rb --dry-run`.
|
|
237
|
+
|
|
238
|
+
`--apply` backs up eligible transcript/metadata bytes under
|
|
239
|
+
`~/.pwn/session_cleanup_archive/<run>/` (directories 0700, files 0600), outside
|
|
240
|
+
`/sessions list`. Atomic, fsynced backups are SHA-256 verified before deletion;
|
|
241
|
+
file identities/hashes, title classification, reference stores and process state
|
|
242
|
+
are rechecked under per-ID/transcript locks. `ledger.json` records exact IDs,
|
|
243
|
+
hashes, skips and completed deletions; dry-run JSON contains metadata only and
|
|
244
|
+
creates no archive. Backups are for recovery, not an automatic reference
|
|
245
|
+
resolver: referenced placeholders remain in the active list until a separately
|
|
246
|
+
verified archive-aware lookup policy exists. No memory, learning or mistake
|
|
247
|
+
records are rewritten. The process checks and advisory locks do not claim a
|
|
248
|
+
transactional guarantee against arbitrary non-cooperating external writers.
|
|
249
|
+
|
|
250
|
+
The cleanup-specific pins additionally preserve every session ID referenced in
|
|
251
|
+
retained learning, mistake and durable memory stores, including non-gold learning
|
|
252
|
+
and old resolved mistakes. References are schema-aware:
|
|
253
|
+
|
|
254
|
+
| Store / writer | Session reference |
|
|
255
|
+
| --- | --- |
|
|
256
|
+
| `learning.jsonl` / Learning; `policy_traj.jsonl` / Policy; `open_goal.json` / OpenGoal | Top-level `session_id` |
|
|
257
|
+
| `mistakes.json` / Mistakes | Each signature record's `sessions[]` and `verified_sessions[]`; signature keys are not session IDs |
|
|
258
|
+
| `memory.json`, `memory.archive.json` / Learning.reflect, Memory | Exact `reflect_<session ID>_<8-hex lesson digest>` keys; arbitrary memory keys, values and source strings are not links |
|
|
259
|
+
| `jobs/<12-hex job ID>.json` / Jobs | Top-level `session_id`; command, environment and job ID are not links |
|
|
260
|
+
| `swarm/*/personas.json` / Swarm | Persona-to-session map values, not persona names or swarm IDs |
|
|
261
|
+
| `curriculum/log.jsonl` / Curriculum | `practice` event's `data[].runs[].session_id`; other events' prose is not traversed |
|
|
262
|
+
| `extrospection.json` / Extrospection | `auto_extrospect` observations' second tag in `['drift', session ID]`; manual/other tags and observation data are not links |
|
|
263
|
+
| `engagements/*/facts.jsonl` / Swarm | Top-level `source_session` |
|
|
264
|
+
| `findings.jsonl` / Findings | Top-level `session_id`; exact transcript/sidecar paths in `evidence_paths[]` and `poc_artifacts[]` |
|
|
265
|
+
| Engagement evidence and recon ledgers / Findings, Recon | Exact transcript/sidecar paths in `path` or `evidence_path`; includes the legacy root `engagements/evidence.jsonl` |
|
|
266
|
+
| `artifacts/manifest.jsonl` / ArtifactRegistry | Top-level `session` and exact transcript/sidecar `source_path`; `kind: swarm-bus` uses `session` for a swarm ID, not session provenance |
|
|
267
|
+
| `artifacts/<session ID>/index.jsonl`, `engagement-memory.json` | Session ownership from that exact directory; index `src` may also reference a transcript/sidecar |
|
|
268
|
+
| `embeddings/<session ID>.db`, `sessions/<session ID>/trace.jsonl` | Ownership from that exact filename/directory, not database contents or trace tool payloads |
|
|
269
|
+
|
|
270
|
+
IDs must be complete canonical values, not substrings or whitespace-normalized
|
|
271
|
+
tokens. Only known evidence/source path fields pointing directly into the
|
|
272
|
+
configured sessions directory are path references. There is currently no
|
|
273
|
+
cross-session link field in transcript or title metadata schemas: listing,
|
|
274
|
+
recalling, quoting JSON, or mentioning a session never pins it, including when
|
|
275
|
+
`sessions_list` output is saved repeatedly. Self listings do not create pins.
|
|
276
|
+
|
|
277
|
+
The index also validates the JSON containers in policy, preference, sentinel,
|
|
278
|
+
skill-review and KPI stores, run checkpoints, mission ledgers, swarm bus/solve
|
|
279
|
+
records, curriculum model/cooldown maps, engagement descriptors/hosts/scans and
|
|
280
|
+
binary facts. These writers do **not** persist session ownership links; their
|
|
281
|
+
arbitrary payloads, prompts, run/job IDs and user-selected labels must not be
|
|
282
|
+
interpreted as such. Unknown JSON stores within the indexed directories are
|
|
283
|
+
reported as uncertainty rather than silently ignored. Adding a new reference
|
|
284
|
+
schema requires auditing its writer/callers and adding positive and negative
|
|
285
|
+
fixtures, not a recursive key-name or text search. Vaults, loot payloads, arbitrary
|
|
286
|
+
artifact content and backups remain outside this index.
|
|
287
|
+
|
|
288
|
+
Native scalar tool `step_reward` fields are recognized;
|
|
289
|
+
unknown transcript fields remain protected.
|
|
290
|
+
|
|
291
|
+
`/sessions list` reads all transcript files afresh, not just cleanup candidates.
|
|
292
|
+
In cleanup output, `unnamed` includes protected sessions; `candidate_ids` excludes
|
|
293
|
+
them. Active sessions hold shared OS locks on permanent per-ID cleanup lock
|
|
294
|
+
files; cleanup takes an exclusive nonblocking lock on the same inode before
|
|
295
|
+
rechecking, backing up and deleting. An active A does not retain inactive B.
|
|
296
|
+
CLI/REPL activation and resume transfer ownership, exit releases it, nested
|
|
297
|
+
Loop requests and title writers hold independent leases, and crashes release
|
|
298
|
+
descriptors automatically. Stale lock contents or reused PIDs confer no ownership.
|
|
299
|
+
Never unlink lock files: that would split the synchronization inode.
|
|
300
|
+
|
|
301
|
+
Old installed processes cannot be upgraded by refreshing the cron helper alone.
|
|
302
|
+
`legacy_owners` reports launchers without the held PID/start-identity capability
|
|
303
|
+
marker; `unknown_legacy_ownership` means their in-memory session IDs are unknown,
|
|
304
|
+
not that every session is active. Ask the operator to identify/quiesce those
|
|
305
|
+
writers and restart using the updated source or deployed version before deleting
|
|
306
|
+
ambiguous candidates. Do not infer active IDs from mtimes, titles or process-name
|
|
307
|
+
matches, and do not bypass reference/summary guards. No config migration is needed.
|
|
308
|
+
|
|
205
309
|
From a checkout:
|
|
206
310
|
|
|
207
311
|
```bash
|