diff --git a/neurosploit-rs/crates/harness/src/pipeline.rs b/neurosploit-rs/crates/harness/src/pipeline.rs index 9d76f39..af83cee 100644 --- a/neurosploit-rs/crates/harness/src/pipeline.rs +++ b/neurosploit-rs/crates/harness/src/pipeline.rs @@ -408,11 +408,19 @@ many registrations, report it as a LEAD and STOP rather than mass-creating accou fn engagement_ops(cfg: &RunConfig) -> String { let (vault, _) = vault_paths(cfg); let temp = if cfg.temp_email { + match std::env::var("NEUROSPLOIT_TEMP_INBOX").ok().filter(|a| !a.trim().is_empty()) { + // The harness already created a disposable inbox (see crate::inbox); + // hand the agent that exact address so the inbox is one the harness + // owns (for cleanup and audit) rather than an unrecorded one. + Some(addr) => Box::leak(format!( + "DISPOSABLE EMAIL (enabled): the harness created inbox `{addr}` for you (mail.tm). USE THIS ADDRESS as the account email. Read confirmation codes/links by polling `GET https://api.mail.tm/messages` โ€” the harness also polls it and records what arrives.", ).into_boxed_str()) as &str, + None => "DISPOSABLE EMAIL (enabled): if registration requires an email confirmation code/link, you MAY use the free \ mail.tm API (no key) โ€” `POST https://api.mail.tm/accounts` {address,password} to create an inbox (get a \ valid domain from `GET https://api.mail.tm/domains`), `POST https://api.mail.tm/token` for a JWT, then poll \ `GET https://api.mail.tm/messages` (Bearer JWT) to read the confirmation code/link. Use the mail.tm address \ as the account email. Guerrilla Mail's API is a fallback. " + } } else { "DISPOSABLE EMAIL (disabled): if registration REQUIRES an email confirmation you cannot receive, stop and \ report it as a blocker (do not attempt to bypass it). " @@ -787,6 +795,24 @@ pub async fn run(cfg: RunConfig, lib: &Library, pool: &ModelPool, tx: Sender { + let addr = inbox.identity(); + std::env::set_var("NEUROSPLOIT_TEMP_INBOX", &addr); + let _ = tx.send(format!("notify: ๐Ÿ“ฌ disposable inbox ready โ€” {addr}")).await; + } + Err(e) => { + let _ = tx.send(format!("notify: โš  could not create a disposable inbox ({e}) โ€” agent will create its own")).await; + } + } + } + // Out-of-band channel. Optional, but without it the blind classes can only // ever be leads โ€” which the prompt says explicitly rather than letting an // agent assert a callback it had no way to receive. @@ -2094,6 +2120,24 @@ async fn finish(cfg: RunConfig, _lib: &Library, pool: &ModelPool, recon: String, if !findings.is_empty() { let _ = tx.send(format!("belief uncertainty over confirmed findings: {:.2} (0=sharp,1=diffuse)", unc)).await; } + // POMDP anti-hallucination gate: a finding asserted as confirmed while the + // belief about it is diffuse or weak is flagged for review. Advisory โ€” it + // never deletes, only surfaces a claim the belief does not yet support. + let pol = crate::pomdp::Policy::default(); + let mut gated = 0usize; + for f in findings.iter_mut() { + if f.validated && f.review_status != "needs-review" && f.review_status != "rejected" { + if let Err(why) = crate::pomdp::may_assert(&wm, &f.id, &pol) { + f.review_status = "needs-review".into(); + f.validated = false; + f.review_reason = if f.review_reason.is_empty() { format!("belief gate: {why}") } else { format!("{} ยท belief gate: {why}", f.review_reason) }; + gated += 1; + } + } + } + if gated > 0 { + let _ = tx.send(format!("belief gate: {gated} finding(s) held for review โ€” asserted above what the evidence supports")).await; + } let _ = tx.send(format!("{} validated finding(s)", findings.len())).await; // Attribution: stamp provenance into each finding (report + json + copies).