diff --git a/README.md b/README.md
index 735a564..fafe74d 100755
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
๐ง NeuroSploit v3.6.1
+๐ง NeuroSploit v3.6.2
@@ -12,7 +12,7 @@
-
+
@@ -28,14 +28,15 @@
>
> ๐ **New here? Read the [full Tutorial & User Guide โ](TUTORIAL.md)** โ every mode, flag, config and example explained.
-> ๐ **New in v3.6.1 โ Cloud testing + REPL navigation + deeper recon:**
-> **AWS/GCP/Azure** agents (+17 โ **375** total) with credentials wired through
-> `creds.yaml`; a more navigable **REPL** โ **`/timeout`** idle guardrail,
-> **multi-target** `/target a,b,c` (sequential), an interactive **`/results`**
-> browser (target โ vuln โ detail, Esc to go back) and **`/report`** picker; and
-> **deeper recon** that downloads & analyzes JavaScript (endpoints, secrets,
-> source maps) and does request/response differential analysis. Interactive
-> line-editing prompt bug fixed.
+> ๐ **New in v3.6.2 โ live Codex streaming + full activity feed:**
+> Codex runs now **stream tool-by-tool** (`codex exec --json`) exactly like
+> Claude Code โ every command, file edit, MCP call and token count appears live
+> instead of a silent black box, so a long intense recon never looks frozen. The
+> **`/logs`** feed and **`/status`** sign-of-line now capture the *actual
+> commands each agent runs* (subfinder, httpx, nmap, curlโฆ), not just pipeline
+> phases. *(v3.6.1 added GPT-5.6 sol/terra/luna, the Codex exit-1 fix, `/logs`
+> and richer `/status`; v3.6.0 added AI-agent/LLM/MCP/Skills/n8n testing +
+> onboarding wizard + Cloud scope.)*
> *(v3.5.4 added robust attack chaining + false-positive reduction; v3.5.3
> GitHub/GitLab/Jira **[integrations](TUTORIAL-INTEGRATION.md)**; v3.5.2 the DEPTH
> doctrine + report-hygiene โ see [RELEASE.md](RELEASE.md).)*
diff --git a/RELEASE.md b/RELEASE.md
index 970fd73..635aff8 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -1,7 +1,7 @@
-# NeuroSploit v3.6.1 โ Release Notes
+# NeuroSploit v3.6.2 โ Release Notes
**Release Date:** July 2026
-**Codename:** GPT-5.6 models
+**Codename:** Live Codex
**License:** MIT
**Credits:** Joas A Santos & Red Team Leaders
@@ -9,6 +9,24 @@
## Highlights
+- **Codex now streams live, tool-by-tool.** `codex exec` is driven with `--json`
+ and its JSONL event stream is parsed into the same categorized activity feed
+ as Claude Code: every shell command it runs (`exec:`), file edit (`edit:`),
+ MCP tool call (`tool:`), web search (`net:`) and token count appears the moment
+ it happens. A long, intense recon (subfinder โ httpx โ katana โ nmap โฆ) is no
+ longer a silent black box โ you watch each tool execute.
+- **`/logs` and `/status` now capture what each agent actually runs.** The
+ activity feed previously dropped the per-agent tool events; it now keeps the
+ actionable ones (commands, network, files, findings) and only filters long
+ model reasoning and token telemetry. `/logs` shows the real command trail;
+ `/status` `last:` shows a true sign-of-life.
+- Failed internal commands surface as `exec: (exit N) ` instead of
+ silently vanishing, and Codex auth/rate errors are still detected from stderr.
+
+---
+
+## Previously in v3.6.1
+
- **Added the GPT-5.6 model line** (OpenAI / ChatGPT): `openai:gpt-5.6-sol`
(frontier / default), `openai:gpt-5.6-terra` (balanced), and
`openai:gpt-5.6-luna` (fast & affordable) โ alongside the existing GPT-5.x,
diff --git a/TUTORIAL.md b/TUTORIAL.md
index 9a19881..537b482 100644
--- a/TUTORIAL.md
+++ b/TUTORIAL.md
@@ -1,4 +1,4 @@
-# NeuroSploit โ Tutorial & User Guide (v3.6.1)
+# NeuroSploit โ Tutorial & User Guide (v3.6.2)
A complete, hands-on guide to installing, configuring and running NeuroSploit โ
the autonomous, multi-model penetration-testing harness.
@@ -98,7 +98,7 @@ Agents **degrade gracefully**: if `rustscan` is absent they use `nmap`; if neith
### Verify
```bash
-neurosploit --version # neurosploit 3.6.1
+neurosploit --version # neurosploit 3.6.2
neurosploit agents # {"vulns":196,...,"chains":12,"total":417}
neurosploit models # all providers & models
```
diff --git a/neurosploit-rs/Cargo.lock b/neurosploit-rs/Cargo.lock
index 660360a..a5b13b5 100644
--- a/neurosploit-rs/Cargo.lock
+++ b/neurosploit-rs/Cargo.lock
@@ -871,7 +871,7 @@ dependencies = [
[[package]]
name = "neurosploit"
-version = "3.6.1"
+version = "3.6.2"
dependencies = [
"anyhow",
"clap",
@@ -888,7 +888,7 @@ dependencies = [
[[package]]
name = "neurosploit-harness"
-version = "3.6.1"
+version = "3.6.2"
dependencies = [
"anyhow",
"futures",
diff --git a/neurosploit-rs/Cargo.toml b/neurosploit-rs/Cargo.toml
index df0ff94..f583193 100644
--- a/neurosploit-rs/Cargo.toml
+++ b/neurosploit-rs/Cargo.toml
@@ -3,7 +3,7 @@ members = ["crates/harness", "app"]
resolver = "2"
[workspace.package]
-version = "3.6.1"
+version = "3.6.2"
edition = "2021"
license = "MIT"
repository = "https://github.com/JoasASantos/NeuroSploit"
diff --git a/neurosploit-rs/app/src/main.rs b/neurosploit-rs/app/src/main.rs
index b362c76..40fc0bc 100644
--- a/neurosploit-rs/app/src/main.rs
+++ b/neurosploit-rs/app/src/main.rs
@@ -1,4 +1,4 @@
-//! NeuroSploit v3.6.1 โ interactive harness + CLI (`run` / `whitebox` / `agents` / `models`).
+//! NeuroSploit v3.6.2 โ interactive harness + CLI (`run` / `whitebox` / `agents` / `models`).
mod repl;
mod tui;
@@ -11,8 +11,8 @@ use std::path::{Path, PathBuf};
#[command(
name = "neurosploit",
version,
- about = "NeuroSploit v3.6.1 โ multi-model autonomous pentest harness",
- long_about = "NeuroSploit v3.6.1 โ a Rust multi-model harness that drives a pool of LLMs \
+ about = "NeuroSploit v3.6.2 โ multi-model autonomous pentest harness",
+ long_about = "NeuroSploit v3.6.2 โ a Rust multi-model harness that drives a pool of LLMs \
(API key or local subscription: Claude/Codex/Gemini/Grok) to autonomously test a target. \
After recon it INTELLIGENTLY selects only the agents matching the discovered surface, runs \
them in parallel, then validates every finding by cross-model voting before reporting.\n\n\
@@ -721,7 +721,7 @@ pub(crate) fn spawn_engagement(base: &Path, mut cfg: RunConfig, mcp: bool, mode:
println!(" โ ua : {ua}");
write_status(&workdir, "running", &format!("\"target\":{:?}", cfg.target));
- println!(" โโ NeuroSploit v3.6.1 ยท by Joas A Santos & Red Team Leaders");
+ println!(" โโ NeuroSploit v3.6.2 ยท by Joas A Santos & Red Team Leaders");
println!(" โ run id : {run_id}");
println!(" โ target : {}", cfg.target);
println!(" โ models : {}", cfg.models.join(", "));
diff --git a/neurosploit-rs/app/src/repl.rs b/neurosploit-rs/app/src/repl.rs
index 8a356b7..6787279 100644
--- a/neurosploit-rs/app/src/repl.rs
+++ b/neurosploit-rs/app/src/repl.rs
@@ -1,4 +1,4 @@
-//! NeuroSploit v3.6.1 โ interactive session (Claude-Code / Codex / Cursor-CLI style).
+//! NeuroSploit v3.6.2 โ interactive session (Claude-Code / Codex / Cursor-CLI style).
//!
//! Launched when `neurosploit` runs with no subcommand. A persistent REPL with
//! real line editing (arrow-key history recall, Ctrl-A/E/K, paste), model
@@ -52,9 +52,16 @@ impl RunLive {
fn ingest(&mut self, line: &str) {
let low = line.to_lowercase();
self.lines += 1;
- // Keep a compact activity trail for /logs and the /status sign-of-life
- // (skip the machine-only finding_json/ai/tokens noise).
- if !low.starts_with("finding_json:") && !low.starts_with("@") && !low.contains("ai:") && !low.starts_with("tokens:") {
+ // Keep a compact activity trail for /logs and the /status sign-of-life.
+ // Streamed agent events are tagged "@label ": keep the actionable
+ // ones (commands, net, tools, file edits, phases) so the operator sees
+ // exactly what each agent is running โ drop only long model reasoning
+ // (ai:), token telemetry (tokens:), and machine JSON (finding_json:).
+ let payload = line.strip_prefix('@')
+ .and_then(|r| r.split_once(' ').map(|(_, rest)| rest))
+ .unwrap_or(line);
+ let plow = payload.to_lowercase();
+ if !low.starts_with("finding_json:") && !plow.starts_with("ai:") && !plow.starts_with("tokens:") {
let clean: String = line.chars().take(160).collect();
self.last = clean.clone();
self.feed.push(clean);
@@ -346,7 +353,7 @@ pub async fn repl(base: &Path) -> anyhow::Result<()> {
let backends = harness::installed_cli_backends();
println!("\x1b[1m");
println!(" โโโโ โโโโโโโโโโโโโโ โโโโโโโโโโ โโโโโโโ");
- println!(" โโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ NeuroSploit v3.6.1");
+ println!(" โโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ NeuroSploit v3.6.2");
println!(" โโโโโโ โโโโโโโโโ โโโ โโโโโโโโโโโโโโ โโโ interactive harness");
println!(" โโโโโโโโโโโโโโโโ โโโ โโโโโโโโโโโโโโ โโโ by Joas A Santos");
println!(" โโโ โโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโ & Red Team Leaders");
diff --git a/neurosploit-rs/app/src/tui.rs b/neurosploit-rs/app/src/tui.rs
index 2673e65..2086288 100644
--- a/neurosploit-rs/app/src/tui.rs
+++ b/neurosploit-rs/app/src/tui.rs
@@ -1,4 +1,4 @@
-//! NeuroSploit v3.6.1 โ TUI "Mission Control" mode.
+//! NeuroSploit v3.6.2 โ TUI "Mission Control" mode.
//!
//! Concurrent panels that update live while the engagement runs in the
//! background, with a composer input that stays active during execution:
diff --git a/neurosploit-rs/crates/harness/src/belief.rs b/neurosploit-rs/crates/harness/src/belief.rs
index 21ff853..38a8c06 100644
--- a/neurosploit-rs/crates/harness/src/belief.rs
+++ b/neurosploit-rs/crates/harness/src/belief.rs
@@ -1,4 +1,4 @@
-//! POMDP belief-state world model (v3.6.1).
+//! POMDP belief-state world model (v3.6.2).
//!
//! The target is only partially observable, so we don't track booleans โ we
//! track a **belief**: a property graph whose nodes (host / service / vuln /
diff --git a/neurosploit-rs/crates/harness/src/grounding.rs b/neurosploit-rs/crates/harness/src/grounding.rs
index fd2d891..c1febba 100644
--- a/neurosploit-rs/crates/harness/src/grounding.rs
+++ b/neurosploit-rs/crates/harness/src/grounding.rs
@@ -1,4 +1,4 @@
-//! Verification / grounding engine (v3.6.1).
+//! Verification / grounding engine (v3.6.2).
//!
//! Hard rule: **no claim enters the world model without a tool receipt** โ raw
//! tool output, not the LLM's paraphrase. This is the empirical anti-hallucination
diff --git a/neurosploit-rs/crates/harness/src/lib.rs b/neurosploit-rs/crates/harness/src/lib.rs
index fc00b14..e64fbe1 100644
--- a/neurosploit-rs/crates/harness/src/lib.rs
+++ b/neurosploit-rs/crates/harness/src/lib.rs
@@ -1,4 +1,4 @@
-//! NeuroSploit v3.6.1 harness โ a robust multi-model runtime for the
+//! NeuroSploit v3.6.2 harness โ a robust multi-model runtime for the
//! markdown-driven autonomous pentest engine.
//!
//! The harness loads the `agents_md/` library, drives a *pool* of LLM models
diff --git a/neurosploit-rs/crates/harness/src/models.rs b/neurosploit-rs/crates/harness/src/models.rs
index 87e5da7..36df2d9 100644
--- a/neurosploit-rs/crates/harness/src/models.rs
+++ b/neurosploit-rs/crates/harness/src/models.rs
@@ -194,6 +194,12 @@ impl ChatClient {
if bin == "claude" {
return self.chat_claude_stream(label, model, &prompt, mcp_config, progress).await;
}
+ // Codex exec streams JSONL events (`--json`): commands it runs, agent
+ // messages, file changes, token usage. Surface them live so recon and
+ // exploitation are visible tool-by-tool instead of a silent black box.
+ if bin == "codex" {
+ return self.chat_codex_stream(label, model, &prompt, mcp_config, progress).await;
+ }
let mut cmd = Command::new(bin);
match bin {
@@ -366,6 +372,136 @@ impl ChatClient {
}
Ok(result)
}
+
+ /// Drive `codex exec --json` and surface its JSONL event stream as a live,
+ /// categorized activity feed (commands, agent messages, file changes, MCP
+ /// tool calls, token usage). The final agent message is returned as the
+ /// result. Mirrors `chat_claude_stream` so Codex runs are just as visible.
+ async fn chat_codex_stream(
+ &self,
+ label: &str,
+ model: &str,
+ prompt: &str,
+ mcp_config: Option<&str>,
+ progress: Option>,
+ ) -> Result {
+ let mut cmd = Command::new("codex");
+ cmd.arg("exec").arg("--json").arg("--model").arg(model)
+ .arg("--dangerously-bypass-approvals-and-sandbox");
+ if let Some(mcp) = mcp_config {
+ for (name, cmdline, args) in mcp_servers_from(mcp) {
+ cmd.arg("-c").arg(format!("mcp_servers.{name}.command={cmdline}"));
+ cmd.arg("-c").arg(format!("mcp_servers.{name}.args={args}"));
+ }
+ }
+ cmd.arg("-");
+ cmd.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).kill_on_drop(true);
+ let mut child = cmd.spawn().map_err(|e| anyhow!("spawn codex failed: {e}"))?;
+ if let Some(mut stdin) = child.stdin.take() {
+ stdin.write_all(prompt.as_bytes()).await?;
+ // Drop closes stdin so Codex processes the prompt and exits.
+ }
+ let stdout = child.stdout.take().ok_or_else(|| anyhow!("no stdout"))?;
+ let stderr = child.stderr.take().ok_or_else(|| anyhow!("no stderr"))?;
+ let mut lines = BufReader::new(stdout).lines();
+ let lbl = if label.is_empty() { String::new() } else { format!("@{label} ") };
+ let emit = |s: String| {
+ if let Some(tx) = &progress {
+ let _ = tx.try_send(format!("{lbl}{s}"));
+ }
+ };
+
+ // Last agent_message is the model's final answer; keep every one so a
+ // run that ends on a tool call still returns the most recent reasoning.
+ let mut result = String::new();
+ let read = async {
+ while let Ok(Some(line)) = lines.next_line().await {
+ let Ok(v) = serde_json::from_str::(&line) else { continue };
+ let ty = v.get("type").and_then(|t| t.as_str()).unwrap_or("");
+ match ty {
+ "item.started" | "item.completed" => {
+ let Some(item) = v.get("item") else { continue };
+ let itype = item.get("type").and_then(|t| t.as_str()).unwrap_or("");
+ match itype {
+ "command_execution" => {
+ // Only announce on start (avoid double lines); note failures on completion.
+ let c = item.get("command").and_then(|x| x.as_str()).unwrap_or("");
+ // Strip the `/bin/sh -lc '...'` wrapper Codex adds.
+ let c = c.strip_prefix("/bin/sh -lc ").map(|s| s.trim_matches('\'')).unwrap_or(c);
+ if ty == "item.started" {
+ let danger = c.contains("rm -rf") || c.contains("mkfs")
+ || c.contains(":(){") || c.contains("dd if=") || c.contains("> /dev/");
+ emit(format!("{}: {}", if danger { "danger" } else { "exec" }, truncate(c, 200)));
+ } else if let Some(code) = item.get("exit_code").and_then(|x| x.as_i64()) {
+ if code != 0 {
+ emit(format!("exec: (exit {code}) {}", truncate(c, 120)));
+ }
+ }
+ }
+ "agent_message" => {
+ if let Some(t) = item.get("text").and_then(|x| x.as_str()) {
+ let t = t.trim();
+ if !t.is_empty() {
+ if ty == "item.completed" { result = t.to_string(); }
+ emit(format!("ai: {}", truncate(t, 240)));
+ }
+ }
+ }
+ "file_change" | "patch" => {
+ let p = item.get("path").and_then(|x| x.as_str())
+ .or_else(|| item.get("file").and_then(|x| x.as_str())).unwrap_or("file");
+ emit(format!("edit: {p}"));
+ }
+ "mcp_tool_call" => {
+ let name = item.get("tool").and_then(|x| x.as_str())
+ .or_else(|| item.get("name").and_then(|x| x.as_str())).unwrap_or("mcp");
+ emit(format!("tool: {name}"));
+ }
+ "web_search" => {
+ let q = item.get("query").and_then(|x| x.as_str()).unwrap_or("");
+ emit(format!("net: search {}", truncate(q, 100)));
+ }
+ _ => {}
+ }
+ }
+ "turn.completed" => {
+ let ti = v.pointer("/usage/input_tokens").and_then(|x| x.as_u64());
+ let to = v.pointer("/usage/output_tokens").and_then(|x| x.as_u64());
+ if ti.is_some() || to.is_some() {
+ emit(format!("tokens: in={} out={}", ti.unwrap_or(0), to.unwrap_or(0)));
+ }
+ }
+ _ => {}
+ }
+ }
+ };
+ // Bound the whole streamed turn (matches the buffered path's cap).
+ if tokio::time::timeout(Duration::from_secs(900), read).await.is_err() {
+ return Err(anyhow!("codex stream timed out after 900s"));
+ }
+ let status = child.wait().await.ok();
+ // Drain stderr for auth/rate diagnostics if we got nothing usable.
+ if result.is_empty() {
+ let mut errbuf = String::new();
+ let mut el = BufReader::new(stderr).lines();
+ while let Ok(Some(l)) = el.next_line().await {
+ if !errbuf.is_empty() { errbuf.push('\n'); }
+ errbuf.push_str(&l);
+ if errbuf.len() > 2000 { break; }
+ }
+ let low = errbuf.to_lowercase();
+ let hard = ["not logged in", "please log in", "please login", "run /login",
+ "unauthorized", "not authenticated", "invalid api key", "no api key",
+ "rate limit", "429", "quota", "credit balance", "usage limit"]
+ .iter().any(|k| low.contains(k));
+ let code = status.and_then(|s| s.code()).map(|c| c.to_string()).unwrap_or_else(|| "signal".into());
+ if hard || !errbuf.trim().is_empty() {
+ return Err(anyhow!("codex subscription CLI exit {}: {}", code, truncate(errbuf.trim(), 240)));
+ }
+ return Err(anyhow!("codex stream produced no result"));
+ }
+ Ok(result)
+ }
}
/// Categorise a Claude tool_use block into a tagged activity-feed event.
diff --git a/neurosploit-rs/crates/harness/src/pomdp.rs b/neurosploit-rs/crates/harness/src/pomdp.rs
index e4891ec..a69f2e4 100644
--- a/neurosploit-rs/crates/harness/src/pomdp.rs
+++ b/neurosploit-rs/crates/harness/src/pomdp.rs
@@ -1,4 +1,4 @@
-//! POMDP decision layer (v3.6.1): value-of-information planning + the
+//! POMDP decision layer (v3.6.2): value-of-information planning + the
//! anti-hallucination gate.
//!
//! The choice "scan more vs exploit now" is **not** a heuristic here โ it falls
diff --git a/neurosploit-rs/crates/harness/src/probe.rs b/neurosploit-rs/crates/harness/src/probe.rs
index c9f6698..2a2d119 100644
--- a/neurosploit-rs/crates/harness/src/probe.rs
+++ b/neurosploit-rs/crates/harness/src/probe.rs
@@ -1,4 +1,4 @@
-//! Deterministic HTTP request/response analysis (v3.6.1).
+//! Deterministic HTTP request/response analysis (v3.6.2).
//!
//! Before the LLM recon runs, the harness performs a **real** probe of the
//! target and captures observed facts โ status, headers, security headers,
diff --git a/neurosploit-rs/crates/harness/src/report.rs b/neurosploit-rs/crates/harness/src/report.rs
index 4e33c3e..6186a49 100644
--- a/neurosploit-rs/crates/harness/src/report.rs
+++ b/neurosploit-rs/crates/harness/src/report.rs
@@ -97,9 +97,9 @@ pub fn html(target: &str, findings: &[Finding]) -> String {
h4{{margin:12px 0 3px;font-size:12px;text-transform:uppercase;letter-spacing:.5px;color:#8b5cf6}}\
.b{{color:#8b5cf6;font-weight:800}}\
NeuroSploit Penetration Test Report
\
- Target: {t} ยท v3.6.1 Rust harness ยท multi-model validated
\
+ Target: {t} ยท v3.6.2 Rust harness ยท multi-model validated
\
{chips}
{graph_block}Findings ({n})
{body}\
- Authorized testing only. Findings confirmed by multi-model adversarial voting.
NeuroSploit v3.6.1 ยท by Joas A Santos & Red Team Leaders