diff --git a/CHANGELOG.md b/CHANGELOG.md index fbbbf48b7..eb38eebaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.1.97] - 2026-06-12 + +### Theme: Application-Layer Permission Engine + +A layered, **fully opt-in** permission system for agent tool calls — the application-layer companion to v0.1.96's OS sandbox. When a `permissions:` block is present, every tool call flows through a hardline catastrophic-command floor, a declarative `allow/ask/deny` rule layer, and a blast-radius risk classifier, resolving to allow / **ask** / deny. An `ask` routes through a pluggable approval provider: an interactive TUI modal (allow once / session / always · reject), an automation policy (`risk-based` / `deny-all` / `allow-all`), or a file request/response handshake for headless/remote approval. Approvals are recorded in an append-only audit ledger; per-agent **role presets** (e.g. `read-only`) scope each agent and also empty the SRT writable set as an OS backstop. A channel-based **guardrail system prompt** tells the model to follow blocks and surface-and-ask rather than circumvent — while keeping `ask` a sanctioned path. **Presence-gated**: a config with no `permissions:` block is 100% unchanged. All items landed under TDD (tests first, confirmed red, then green), with live verification across automation runs. **Honest scope**: the prompt + regex classifier are best-effort *alignment*, not enforcement — the OS sandbox (v0.1.96) remains the load-bearing control (see `docs/dev_notes/permissions_p2_followups.md`). + +### Added +- **Permission engine (opt-in `permissions:` block)**: composite `PreToolUse` pipeline in `massgen/permissions/` — a non-overridable hardline blocklist (`hardline.py`, catastrophic patterns like `rm -rf /`, fork bombs, raw-disk `dd`), a declarative `action(target)` rule layer (`rules.py`: `command`/`read_file`/`write_file`/`read_url`/`mcp`/`*`, **deny-wins** across scopes), and a blast-radius `RiskClassifier` (`risk_classifier.py`: tiers by what the call *does* — egress/force-push/publish/privilege → high, reads/in-workspace edits → low). An explicit rule suppresses the risk-ask, so rules + risk live in one hook. +- **Approval round-trip**: the `base_with_custom_tool_and_mcp` chokepoint resolves an `ask` via a pluggable `ApprovalProvider` — `CallbackApprovalProvider` → interactive **TUI modal** (`ToolApprovalModal`: allow once/session/always · reject), `PolicyApprovalProvider` → automation default (`risk-based` ships default; high denied with reason, low/medium allowed), and `FileApprovalProvider` → `req_*.json`/`resp_*.json` handshake for headless/remote (fail-closed on timeout). +- **Per-agent role scoping**: `permissions.role` presets (`read-only`/`researcher` deny writes+shell; `read-write`/`implementer` fall through to rules+risk), merged with user rules deny-wins. A `read-only` role also empties the agent's SRT writable set (OS-layer backstop to the engine's write denials). +- **Audit ledger + runaway guard (`ledger.py`)**: `ApprovalLedger` writes one append-only JSONL line per approval decision (who/what/why/outcome, crash-safe). `ApprovalBudget` caps consecutive auto-approvals per agent (opt-in `max_consecutive_auto`; fail-closed past the cap, reset by any human decision). +- **`always`-grant persistence**: an operator's "Always" approval persists as a deduped `allow(...)` rule in `settings.local.json` and loads back as a merged scope next run (opt-out `persist_approvals: false`). +- **Channel-based guardrail system prompt**: `PermissionGuardrailSection` is injected into the system prompt only when the engine is active for that agent — follow the guardrails, don't circumvent a denial (no rewording/obfuscation/tool-swap), surface-and-ask instead; **`ask` is explicitly sanctioned, not a block**. Authority is established by channel (only the system prompt is authoritative; tool/file/web content never is) — no token needed. + +### Changed +- **Denied tool calls render as first-class FAILED tool events**: the deny path now emits `tool_start` (with the attempted command/args) + `tool_complete(is_error=True, status="denied")` and the status line shows the command (`Denied ($ curl …): …`), so blocked calls appear in the TUI/WebUI timeline instead of only a transient status line. + +### Fixed +- **Backend parity guard**: native backends (`claude_code`, `codex`) don't run the framework `PreToolUse` chokepoint, so a `permissions:` block there is reported **INACTIVE** at startup (loud warning) and inert hooks are skipped — preventing a false promise of enforcement. + +### Tests +- New deterministic suites: `test_permissions_core.py`, `test_permission_rules.py`, `test_permission_hooks.py`, `test_permission_coordinator.py`, `test_approval_provider.py` / `test_file_approval_provider.py`, `test_approval_ledger.py`, `test_tool_approval_modal.py`, `test_permissions_optional.py` (opt-in/presence gate + parity guard), `test_permission_persistence.py` (write↔load roundtrip + dedup), `test_permission_guardrail_prompt.py` (gating + content incl. ask-is-sanctioned), `test_permission_denied_tool_visibility.py` (start→error-complete events + command preview), plus SRT read-only backstop in `test_srt_manager.py` / `test_srt_filesystem_integration.py`. +- Live-verified (automation, `gemini-3-flash-preview`): all three chokepoint branches end-to-end (allow / deny-rule / ask→policy-deny + ledger), guardrail policy present in the real system message, denied calls emitting real `tool_start`/`tool_complete(error)` events with the command. Documented honest limitation: the model evaded the regex egress classifier via `\c\u\r\l` / `python urllib`, confirming the OS sandbox is the load-bearing control. + +### Documentations, Configurations and Resources +- **New Configs**: `massgen/configs/tools/permissions/permission_engine.yaml` (risk-tiered approval + rule algebra), `per_agent_roles.yaml` (role scoping), `permission_modal_interactive.yaml` (interactive approval-modal demo + automation deny path). +- **Design Notes**: `docs/dev_notes/permission_systems_research.md` (three-layer model), `docs/dev_notes/permissions_p2_followups.md` (limitations, manual-test gaps, OS-enforcement follow-up). + ## [0.1.96] - 2026-06-10 ### Theme: OS-Level Agent Sandboxing diff --git a/README.md b/README.md index c71cad92c..88964eaac 100644 --- a/README.md +++ b/README.md @@ -122,8 +122,8 @@ This project started with the "threads of thought" and "iterative refinement" id

🗺️ Roadmap

-- [Recent Achievements (v0.1.96)](#recent-achievements-v0196) -- [Previous Achievements (v0.0.3 - v0.1.95)](#previous-achievements-v003---v0195) +- [Recent Achievements (v0.1.97)](#recent-achievements-v0197) +- [Previous Achievements (v0.0.3 - v0.1.96)](#previous-achievements-v003---v0196) - [Key Future Enhancements](#key-future-enhancements) - Bug Fixes & Backend Improvements - Advanced Agent Collaboration @@ -155,18 +155,18 @@ This project started with the "threads of thought" and "iterative refinement" id --- -## 🆕 Latest Features (v0.1.96) +## 🆕 Latest Features (v0.1.97) -**🎉 Released: June 10, 2026** +**🎉 Released: June 12, 2026** -**What's New in v0.1.96** (OS-Level Agent Sandboxing): -- **🛡️ OS-Level Execution Sandbox** - Confine agent command/code execution at the OS level via Anthropic's [sandbox-runtime](https://github.com/anthropic-experimental/sandbox-runtime) (bubblewrap on Linux, Seatbelt on macOS) with one knob: `command_line_execution_mode: srt`. Filesystem + network isolation derived from the same path policy as the application layer — **defense in depth**, both layers active. -- **🔒 Configurable Read Confinement** - By default (`confined`), sandboxed commands can't read your `$HOME` (secrets, other projects) — only the workspace + context — while system paths stay readable so commands still run. `strict` and `open` modes available. Network is **deny-all by default**; each allowlisted domain is an explicit capability grant. -- **🧱 Hardened Permission Hook** - A key-agnostic scan walks the full tool-args tree (nested dicts + lists) and denies any value resolving outside managed areas, closing prior fail-open gaps (unrecognized key, list-valued paths, `move`/`copy` source pointing outside) — with no false positives. +**What's New in v0.1.97** (Application-Layer Permission Engine): +- **🛡️ Layered Permission Engine** - Opt-in `permissions:` block routes every tool call through a non-overridable **hardline** floor (`rm -rf /`, fork bombs), declarative **`allow/ask/deny` rules** over a small `action(target)` algebra (deny-wins), and a **blast-radius risk classifier** — auto-allowing reads/in-workspace edits and asking only for the dangerous tail (egress, force-push, publish, privilege). The app-layer companion to v0.1.96's OS sandbox. +- **✋ Approval That Fits the Run** - An `ask` pops an interactive **modal** (allow once / session / always · reject) when a human is present, or resolves via an automation **policy** (`risk-based` / `deny-all` / `allow-all`) or a **file** request/response handshake for headless/remote approval. Fail-closed by design. +- **🧑‍🤝‍🧑 Roles, Audit & Guards** - Per-agent `role` presets (e.g. `read-only`, which also empties the agent's OS-sandbox writable set), an append-only JSONL **audit ledger** of every decision, a runaway-loop **budget**, `always`-grant persistence, and a channel-based **guardrail prompt** that nudges the model to surface blocks rather than circumvent them while keeping `ask` sanctioned. *(Honest scope: the prompt is best-effort alignment; the OS sandbox is the enforcement.)* -**Install v0.1.96:** +**Install v0.1.97:** ```bash -pip install massgen==0.1.96 +pip install massgen==0.1.97 ``` → [See full release history and examples](massgen/configs/README.md#release-history--examples) @@ -1241,18 +1241,20 @@ MassGen is currently in its foundational stage, with a focus on parallel, asynch ⚠️ **Early Stage Notice:** As MassGen is in active development, please expect upcoming breaking architecture changes as we continue to refine and improve the system. -### Recent Achievements (v0.1.96) +### Recent Achievements (v0.1.97) -**🎉 Released: June 10, 2026** +**🎉 Released: June 12, 2026** -#### OS-Level Agent Sandboxing -- **OS-Level Execution Sandbox (`command_line_execution_mode: srt`)**: a third execution mode alongside `local`/`docker` that wraps agent command/code execution in Anthropic's [sandbox-runtime](https://github.com/anthropic-experimental/sandbox-runtime) (bubblewrap on Linux, Seatbelt on macOS). `SrtManager` (`massgen/filesystem_manager/_srt_manager.py`) derives per-agent OS-enforced filesystem + network isolation from the **same** `PathPermissionManager` policy as the app layer — defense in depth, both layers active. Default-off, one-knob opt-in -- **Configurable Read Confinement (`command_line_srt_read_mode`, default `confined`)**: SRT reads are allow-all by default, so `confined` denies all of `$HOME` and re-allows only the workspace + context (system paths stay readable so commands run); `strict` denies `/` and allows only managed + a system baseline; `open` allows-all minus a secret denylist. `command_line_srt_allow_read` widens it per config. **Network is deny-all by default** — each allowlisted domain is an explicit capability grant -- **Hardened Permission Hook**: a new key-agnostic scan (`_validate_no_path_arg_escapes`) walks the full tool-args tree (nested dicts + lists) and denies any value resolving outside managed areas, closing prior fail-open gaps (path under an unrecognized key, list-valued paths, `move`/`copy` source pointing outside) without false positives -- **Parity & safety**: native-sandbox backends (Codex `--full-auto`, Claude Code) degrade `srt`→`local` to avoid nested-sandbox hangs; subagents inherit the parent's SRT settings (parity with Docker); live-verified across OpenRouter, OpenAI Responses, and Gemini backends -- All landed under TDD, with a 15-vector adversarial escape suite and an adversarially-verified multi-agent pre-merge review +#### Application-Layer Permission Engine +- **Permission engine (opt-in `permissions:` block)**: a composite `PreToolUse` pipeline in `massgen/permissions/` — a non-overridable **hardline** blocklist (`hardline.py`: `rm -rf /`, fork bombs, raw-disk `dd`), a declarative **`action(target)` rule layer** (`rules.py`: `command`/`read_file`/`write_file`/`read_url`/`mcp`/`*`, deny-wins across scopes), and a **blast-radius `RiskClassifier`** that tiers by what the call does (egress/force-push/publish/privilege → high; reads/in-workspace edits → low). An explicit rule suppresses the risk-ask, so rules + risk live in one hook +- **Approval round-trip**: the `base_with_custom_tool_and_mcp` chokepoint resolves an `ask` via a pluggable `ApprovalProvider` — interactive **modal** (`ToolApprovalModal`: allow once/session/always · reject), automation **policy** (`risk-based` default / `deny-all` / `allow-all`), or **file** request/response handshake for headless/remote — fail-closed on timeout +- **Roles, audit & guards**: per-agent `role` presets (`read-only`/`researcher` deny writes+shell, also empties the agent's SRT writable set), an append-only JSONL **`ApprovalLedger`**, a runaway-loop **`ApprovalBudget`**, and `always`-grant persistence to `settings.local.json` +- **Guardrail system prompt** (`PermissionGuardrailSection`, injected only when the engine is active): follow the guardrails, don't circumvent a denial, surface-and-ask — while keeping `ask` a sanctioned path. Authority is established by channel (only the system prompt is authoritative). Denied tool calls now render as **first-class failed tool events** (with the command) in the TUI/WebUI timeline +- **Presence-gated & honest**: a config with no `permissions:` block is 100% unchanged; native backends (claude_code/codex) report **INACTIVE** rather than silently inert. All under TDD; live-verified that the prompt is best-effort *alignment* (a model evaded the regex egress classifier via `\c\u\r\l` / `python urllib`), so the OS sandbox remains the load-bearing enforcement -### Previous Achievements (v0.0.3 - v0.1.95) +### Previous Achievements (v0.0.3 - v0.1.96) + +✅ **OS-Level Agent Sandboxing (v0.1.96)**: Added a real OS-level execution sandbox (`command_line_execution_mode: srt`) wrapping agent command/code execution in Anthropic's sandbox-runtime (bubblewrap/Seatbelt), with OS-enforced filesystem + network isolation derived from the same `PathPermissionManager` policy as the app layer (defense in depth), configurable read confinement (`confined`/`strict`/`open`, deny-all network), a hardened key-agnostic permission hook, native-sandbox-backend `srt`→`local` degrade, and subagent inheritance. ✅ **Steering Improvements (v0.1.95)**: Extended mid-stream steering from a UI-only capability into a programmatic, headless one — `send_steering_message()` drops guidance into a file inbox (`--inbox-dir`) routed through the same `set_pending_input` chokepoint the TUI/WebUI use — and upgraded Codex/Antigravity to interrupt-and-resume the in-flight turn (`codex exec resume` / `agy --continue`) instead of waiting for a round boundary, with `expires_at`-guarded MCP-hook payload IPC and the Antigravity `--model` flag wired through. diff --git a/README_PYPI.md b/README_PYPI.md index 91d68cfb0..b5745cb7f 100644 --- a/README_PYPI.md +++ b/README_PYPI.md @@ -121,8 +121,8 @@ This project started with the "threads of thought" and "iterative refinement" id

🗺️ Roadmap

-- [Recent Achievements (v0.1.96)](#recent-achievements-v0196) -- [Previous Achievements (v0.0.3 - v0.1.95)](#previous-achievements-v003---v0195) +- [Recent Achievements (v0.1.97)](#recent-achievements-v0197) +- [Previous Achievements (v0.0.3 - v0.1.96)](#previous-achievements-v003---v0196) - [Key Future Enhancements](#key-future-enhancements) - Bug Fixes & Backend Improvements - Advanced Agent Collaboration @@ -154,18 +154,18 @@ This project started with the "threads of thought" and "iterative refinement" id --- -## 🆕 Latest Features (v0.1.96) +## 🆕 Latest Features (v0.1.97) -**🎉 Released: June 10, 2026** +**🎉 Released: June 12, 2026** -**What's New in v0.1.96** (OS-Level Agent Sandboxing): -- **🛡️ OS-Level Execution Sandbox** - Confine agent command/code execution at the OS level via Anthropic's [sandbox-runtime](https://github.com/anthropic-experimental/sandbox-runtime) (bubblewrap on Linux, Seatbelt on macOS) with one knob: `command_line_execution_mode: srt`. Filesystem + network isolation derived from the same path policy as the application layer — **defense in depth**, both layers active. -- **🔒 Configurable Read Confinement** - By default (`confined`), sandboxed commands can't read your `$HOME` (secrets, other projects) — only the workspace + context — while system paths stay readable so commands still run. `strict` and `open` modes available. Network is **deny-all by default**; each allowlisted domain is an explicit capability grant. -- **🧱 Hardened Permission Hook** - A key-agnostic scan walks the full tool-args tree (nested dicts + lists) and denies any value resolving outside managed areas, closing prior fail-open gaps (unrecognized key, list-valued paths, `move`/`copy` source pointing outside) — with no false positives. +**What's New in v0.1.97** (Application-Layer Permission Engine): +- **🛡️ Layered Permission Engine** - Opt-in `permissions:` block routes every tool call through a non-overridable **hardline** floor (`rm -rf /`, fork bombs), declarative **`allow/ask/deny` rules** over a small `action(target)` algebra (deny-wins), and a **blast-radius risk classifier** — auto-allowing reads/in-workspace edits and asking only for the dangerous tail (egress, force-push, publish, privilege). The app-layer companion to v0.1.96's OS sandbox. +- **✋ Approval That Fits the Run** - An `ask` pops an interactive **modal** (allow once / session / always · reject) when a human is present, or resolves via an automation **policy** (`risk-based` / `deny-all` / `allow-all`) or a **file** request/response handshake for headless/remote approval. Fail-closed by design. +- **🧑‍🤝‍🧑 Roles, Audit & Guards** - Per-agent `role` presets (e.g. `read-only`, which also empties the agent's OS-sandbox writable set), an append-only JSONL **audit ledger** of every decision, a runaway-loop **budget**, `always`-grant persistence, and a channel-based **guardrail prompt** that nudges the model to surface blocks rather than circumvent them while keeping `ask` sanctioned. *(Honest scope: the prompt is best-effort alignment; the OS sandbox is the enforcement.)* -**Install v0.1.96:** +**Install v0.1.97:** ```bash -pip install massgen==0.1.96 +pip install massgen==0.1.97 ``` → [See full release history and examples](massgen/configs/README.md#release-history--examples) @@ -1240,18 +1240,20 @@ MassGen is currently in its foundational stage, with a focus on parallel, asynch ⚠️ **Early Stage Notice:** As MassGen is in active development, please expect upcoming breaking architecture changes as we continue to refine and improve the system. -### Recent Achievements (v0.1.96) +### Recent Achievements (v0.1.97) -**🎉 Released: June 10, 2026** +**🎉 Released: June 12, 2026** -#### OS-Level Agent Sandboxing -- **OS-Level Execution Sandbox (`command_line_execution_mode: srt`)**: a third execution mode alongside `local`/`docker` that wraps agent command/code execution in Anthropic's [sandbox-runtime](https://github.com/anthropic-experimental/sandbox-runtime) (bubblewrap on Linux, Seatbelt on macOS). `SrtManager` (`massgen/filesystem_manager/_srt_manager.py`) derives per-agent OS-enforced filesystem + network isolation from the **same** `PathPermissionManager` policy as the app layer — defense in depth, both layers active. Default-off, one-knob opt-in -- **Configurable Read Confinement (`command_line_srt_read_mode`, default `confined`)**: SRT reads are allow-all by default, so `confined` denies all of `$HOME` and re-allows only the workspace + context (system paths stay readable so commands run); `strict` denies `/` and allows only managed + a system baseline; `open` allows-all minus a secret denylist. `command_line_srt_allow_read` widens it per config. **Network is deny-all by default** — each allowlisted domain is an explicit capability grant -- **Hardened Permission Hook**: a new key-agnostic scan (`_validate_no_path_arg_escapes`) walks the full tool-args tree (nested dicts + lists) and denies any value resolving outside managed areas, closing prior fail-open gaps (path under an unrecognized key, list-valued paths, `move`/`copy` source pointing outside) without false positives -- **Parity & safety**: native-sandbox backends (Codex `--full-auto`, Claude Code) degrade `srt`→`local` to avoid nested-sandbox hangs; subagents inherit the parent's SRT settings (parity with Docker); live-verified across OpenRouter, OpenAI Responses, and Gemini backends -- All landed under TDD, with a 15-vector adversarial escape suite and an adversarially-verified multi-agent pre-merge review +#### Application-Layer Permission Engine +- **Permission engine (opt-in `permissions:` block)**: a composite `PreToolUse` pipeline in `massgen/permissions/` — a non-overridable **hardline** blocklist (`hardline.py`: `rm -rf /`, fork bombs, raw-disk `dd`), a declarative **`action(target)` rule layer** (`rules.py`: `command`/`read_file`/`write_file`/`read_url`/`mcp`/`*`, deny-wins across scopes), and a **blast-radius `RiskClassifier`** that tiers by what the call does (egress/force-push/publish/privilege → high; reads/in-workspace edits → low). An explicit rule suppresses the risk-ask, so rules + risk live in one hook +- **Approval round-trip**: the `base_with_custom_tool_and_mcp` chokepoint resolves an `ask` via a pluggable `ApprovalProvider` — interactive **modal** (`ToolApprovalModal`: allow once/session/always · reject), automation **policy** (`risk-based` default / `deny-all` / `allow-all`), or **file** request/response handshake for headless/remote — fail-closed on timeout +- **Roles, audit & guards**: per-agent `role` presets (`read-only`/`researcher` deny writes+shell, also empties the agent's SRT writable set), an append-only JSONL **`ApprovalLedger`**, a runaway-loop **`ApprovalBudget`**, and `always`-grant persistence to `settings.local.json` +- **Guardrail system prompt** (`PermissionGuardrailSection`, injected only when the engine is active): follow the guardrails, don't circumvent a denial, surface-and-ask — while keeping `ask` a sanctioned path. Authority is established by channel (only the system prompt is authoritative). Denied tool calls now render as **first-class failed tool events** (with the command) in the TUI/WebUI timeline +- **Presence-gated & honest**: a config with no `permissions:` block is 100% unchanged; native backends (claude_code/codex) report **INACTIVE** rather than silently inert. All under TDD; live-verified that the prompt is best-effort *alignment* (a model evaded the regex egress classifier via `\c\u\r\l` / `python urllib`), so the OS sandbox remains the load-bearing enforcement -### Previous Achievements (v0.0.3 - v0.1.95) +### Previous Achievements (v0.0.3 - v0.1.96) + +✅ **OS-Level Agent Sandboxing (v0.1.96)**: Added a real OS-level execution sandbox (`command_line_execution_mode: srt`) wrapping agent command/code execution in Anthropic's sandbox-runtime (bubblewrap/Seatbelt), with OS-enforced filesystem + network isolation derived from the same `PathPermissionManager` policy as the app layer (defense in depth), configurable read confinement (`confined`/`strict`/`open`, deny-all network), a hardened key-agnostic permission hook, native-sandbox-backend `srt`→`local` degrade, and subagent inheritance. ✅ **Steering Improvements (v0.1.95)**: Extended mid-stream steering from a UI-only capability into a programmatic, headless one — `send_steering_message()` drops guidance into a file inbox (`--inbox-dir`) routed through the same `set_pending_input` chokepoint the TUI/WebUI use — and upgraded Codex/Antigravity to interrupt-and-resume the in-flight turn (`codex exec resume` / `agy --continue`) instead of waiting for a round boundary, with `expires_at`-guarded MCP-hook payload IPC and the Antigravity `--model` flag wired through. diff --git a/RELEASE_NOTES_v0.1.97.md b/RELEASE_NOTES_v0.1.97.md new file mode 100644 index 000000000..921a2ea39 --- /dev/null +++ b/RELEASE_NOTES_v0.1.97.md @@ -0,0 +1,52 @@ +# 🚀 Release Highlights — v0.1.97 (2026-06-12) + +**Theme: Application-Layer Permission Engine** — the opt-in approval pipeline that complements v0.1.96's OS sandbox. Defense in depth: the OS layer enforces, the app layer keeps a human in the loop on the risky calls. + +### 🛡️ Layered permission engine (hardline → rules → risk) +- **Hardline floor**: a non-overridable blocklist denies catastrophic commands (`rm -rf /`, fork bombs, raw-disk `dd`) regardless of any rule or mode. +- **Declarative rules**: an `allow/ask/deny` algebra over a small `action(target)` vocabulary (`command`, `read_file`, `write_file`, `read_url`, `mcp`, `*`) with **deny-wins** precedence across scopes. +- **Risk classifier**: tiers a call by blast radius, not name — auto-allows reads and in-workspace edits, asks only for the dangerous tail (network egress, force-push, publish/spend, privilege escalation). + +### ✋ Approval that fits the run +- **Interactive modal** (`ToolApprovalModal`): allow once / allow session / always · reject, when a human is present. +- **Automation policy**: `risk-based` (default — high denied with a reason, low/medium allowed), `deny-all`, or `allow-all`. +- **File handshake** (`FileApprovalProvider`): `req_*.json` / `resp_*.json` for headless/remote approval (Slack bot, `/approve `, …). Fail-closed on timeout throughout. + +### 🧑‍🤝‍🧑 Roles, audit & guards +- **Per-agent roles**: `read-only` / `researcher` deny writes+shell; a `read-only` role also empties the agent's SRT writable set (OS backstop). Role + user rules merge deny-wins. +- **Audit ledger**: every approval decision is appended to a crash-safe JSONL trail (who/what/why/outcome). +- **Runaway-loop budget**: `max_consecutive_auto` caps consecutive auto-approvals per agent and fail-closes past the cap; a human decision resets it. +- **`always`-grant persistence**: an "Always" approval persists as an `allow(...)` rule in `settings.local.json` and loads back next run. + +### 🧭 Guardrail-aware system prompt (channel-based) +- When permissions are active, the system prompt tells the model to **follow blocks and surface-and-ask rather than circumvent** them — and that **`ask` is a sanctioned path**, not a block. Authority comes only from the system prompt (no leakable token). +- **Honest scope**: the prompt + regex classifier are best-effort *alignment*. Live runs showed a model evading the egress classifier via `\c\u\r\l` / `python urllib` — so the OS sandbox (v0.1.96) remains the load-bearing enforcement. See `docs/dev_notes/permissions_p2_followups.md`. + +### 🔧 Also in this release +- **Denied tool calls are first-class failed tool events**: the deny path emits `tool_start` (with the attempted command) + `tool_complete(is_error=True, status="denied")`, so blocked calls show in the TUI/WebUI timeline with the command, not just a status line. +- **Backend parity guard**: native backends (`claude_code`, `codex`) don't run the framework chokepoint, so a `permissions:` block there is reported **INACTIVE** at startup instead of silently inert. + +--- + +### 📖 Getting Started +- [**Quick Start Guide**](https://github.com/massgen/MassGen?tab=readme-ov-file#1--installation): upgrade and try the permission engine. +- **Try the approval modal (interactive):** + +```bash +# A high-risk command pops the approval modal (allow once/session/always · reject) +uv run massgen --config massgen/configs/tools/permissions/permission_modal_interactive.yaml \ + "Run the shell command: curl -s https://example.com" +``` + +- **Try risk-tiered automation (headless deny):** + +```bash +uv run massgen --automation --config massgen/configs/tools/permissions/permission_engine.yaml \ + "Run 'git status', then run 'git push --force origin main' and report each result." +# Expected: git status runs; the force-push is denied with a reason. +``` + +## What's Changed +* feat: layered opt-in permission system (P0–P2.2) — rules, approval, audit, guardrail prompt by @ncrispino in https://github.com/massgen/MassGen/pull/1127 + +**Full Changelog**: https://github.com/massgen/MassGen/compare/v0.1.96...v0.1.97 diff --git a/ROADMAP.md b/ROADMAP.md index ee65e85c4..c3a9b0ada 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,10 +1,10 @@ # MassGen Roadmap -**Current Version:** v0.1.96 +**Current Version:** v0.1.97 **Release Schedule:** Mondays, Wednesdays, Fridays @ 9am PT -**Last Updated:** June 10, 2026 +**Last Updated:** June 12, 2026 This roadmap outlines MassGen's development priorities for upcoming releases. Each release focuses on specific capabilities with real-world use cases. @@ -42,12 +42,31 @@ Want to contribute or collaborate on a specific track? Reach out to the track ow | Release | Target | Feature | Owner | Use Case | |---------|--------|---------|-------|----------| -| **v0.1.97** | TBD | Image/Video Edit Capabilities | @ncrispino | Check and support img/video editing capabilities — deferred from v0.1.86-v0.1.96 ([#959](https://github.com/massgen/MassGen/issues/959)) | +| **v0.1.98** | TBD | Image/Video Edit Capabilities | @ncrispino | Check and support img/video editing capabilities — deferred from v0.1.86-v0.1.97 ([#959](https://github.com/massgen/MassGen/issues/959)) | *All releases ship on MWF @ 9am PT when ready* --- +## ✅ v0.1.97 - Application-Layer Permission Engine (Completed) + +**Released:** June 12, 2026 + +### Features +- **Permission engine (opt-in `permissions:` block)**: a composite `PreToolUse` pipeline in `massgen/permissions/` — a non-overridable **hardline** blocklist (`hardline.py`: catastrophic patterns like `rm -rf /`, fork bombs, raw-disk `dd`), a declarative **`action(target)` rule layer** (`rules.py`: `command`/`read_file`/`write_file`/`read_url`/`mcp`/`*`, deny-wins across scopes), and a **blast-radius `RiskClassifier`** that tiers by what the call does (egress/force-push/publish/privilege → high; reads/in-workspace edits → low). An explicit rule suppresses the risk-ask, so rules + risk live in one hook +- **Approval round-trip**: the `base_with_custom_tool_and_mcp` chokepoint resolves an `ask` via a pluggable `ApprovalProvider` — interactive **modal** (`ToolApprovalModal`: allow once/session/always · reject), automation **policy** (`risk-based` default / `deny-all` / `allow-all`), or **file** request/response handshake for headless/remote (fail-closed on timeout) +- **Roles, audit & guards**: per-agent `role` presets (`read-only`/`researcher` deny writes+shell, also empty the agent's SRT writable set), an append-only JSONL **`ApprovalLedger`**, a runaway-loop **`ApprovalBudget`** (opt-in `max_consecutive_auto`), and `always`-grant persistence to `settings.local.json` +- **Channel-based guardrail system prompt** (`PermissionGuardrailSection`, injected only when the engine is active): follow the guardrails, don't circumvent a denial, surface-and-ask — while keeping `ask` a sanctioned path. Denied tool calls now render as **first-class failed tool events** (with the command) in the TUI/WebUI timeline +- **Backend parity guard**: native backends (`claude_code`, `codex`) lack the framework chokepoint, so a `permissions:` block there is reported **INACTIVE** instead of silently inert + +### Notes +- All items landed under TDD (tests first, confirmed red, then green); presence-gated so a config with no `permissions:` block is 100% unchanged. +- Live-verified (automation, `gemini-3-flash-preview`): all three chokepoint branches end-to-end + audit ledger; denied calls emitting real `tool_start`/`tool_complete(error)` events. +- **Honest scope**: the prompt + regex classifier are best-effort *alignment* — a model evaded the egress classifier via `\c\u\r\l` / `python urllib`, confirming the OS sandbox (v0.1.96) is the load-bearing enforcement. Follow-ups in `docs/dev_notes/permissions_p2_followups.md` (limitations) and OS-layer egress enforcement. +- Image/Video Edit Capabilities ([#959](https://github.com/massgen/MassGen/issues/959)) remain deferred to v0.1.98. + +--- + ## ✅ v0.1.96 - OS-Level Agent Sandboxing (Completed) **Released:** June 10, 2026 diff --git a/ROADMAP_v0.1.98.md b/ROADMAP_v0.1.98.md index c37c2ff3b..a57aeb842 100644 --- a/ROADMAP_v0.1.98.md +++ b/ROADMAP_v0.1.98.md @@ -1,14 +1,14 @@ -# MassGen v0.1.97 Roadmap +# MassGen v0.1.98 Roadmap **Target Release:** TBD ## Overview -Version 0.1.97 picks up the image/video edit work deferred from v0.1.86-v0.1.96 and continues multimodal provider-parity work. +Version 0.1.98 picks up the image/video edit work deferred from v0.1.86-v0.1.97 and continues multimodal provider-parity work. --- -## Feature: Image/Video Edit Capabilities (Deferred from v0.1.86-v0.1.96) +## Feature: Image/Video Edit Capabilities (Deferred from v0.1.86-v0.1.97) **Issue:** [#959](https://github.com/massgen/MassGen/issues/959) **Owner:** @ncrispino diff --git a/docs/announcements/archive/v0.1.96.md b/docs/announcements/archive/v0.1.96.md new file mode 100644 index 000000000..d7baab5a2 --- /dev/null +++ b/docs/announcements/archive/v0.1.96.md @@ -0,0 +1,54 @@ +# MassGen v0.1.96 Release Announcement (OS-Level Agent Sandboxing) + + + +## Release Summary + +MassGen v0.1.96 — OS-Level Agent Sandboxing! 🚀 Agents that run commands can now be confined at the OS level via Anthropic's [sandbox-runtime](https://github.com/anthropic-experimental/sandbox-runtime) (`srt`), with a hardened permission hook on top. Defense in depth: OS and app layers from the same path policy, both active. Default-off, one knob (`command_line_execution_mode: srt`). + +## Install + +```bash +pip install massgen==0.1.96 +``` + +## Links + +- **Release notes:** https://github.com/massgen/MassGen/releases/tag/v0.1.96 +- **X post:** [TO BE ADDED AFTER POSTING] +- **LinkedIn post:** [TO BE ADDED AFTER POSTING] + +## Posting Notes + +- **Suggested image:** A terminal screenshot of the `srt_sandbox.yaml` demo run — agent writes to its workspace successfully, then an out-of-workspace read (`~/.ssh/id_rsa`) and network egress are both denied with `Operation not permitted`. This is a headless/security feature, so a clean before/after terminal capture beats a TUI screenshot. + +--- + +## Full Announcement (for LinkedIn) + +Copy everything below this line, then append content from `feature-highlights.md`: + +--- + +MassGen v0.1.96 — OS-Level Agent Sandboxing! 🚀 Agents that run commands can now be confined at the OS level, not just by MassGen's permission layer. Both layers derive from the same path policy and stay active together, closing the shell and file-tool escape hatches at once. Default-off, opt-in with a single knob. + +**Key Improvements:** + +🛡️ **OS-level execution sandbox** — `command_line_execution_mode: srt` wraps agent command/code execution in Anthropic's sandbox-runtime (bubblewrap on Linux, Seatbelt on macOS) for OS-enforced filesystem + network isolation, derived from the same permission policy as the app layer. Network is deny-all by default. + +🔒 **Configurable read confinement** — by default (`confined`), sandboxed commands can't read your `$HOME` (secrets, other projects), only the workspace + context, while system paths stay readable so commands still run. + +🧱 **Hardened permission hook** — a key-agnostic scan walks the full tool-args tree and denies any path resolving outside managed areas, closing prior fail-open gaps with no false positives. + +**Install:** + +```bash +pip install massgen==0.1.96 +``` + +Feature highlights: + + diff --git a/docs/announcements/current-release.md b/docs/announcements/current-release.md index d7baab5a2..478e3ee18 100644 --- a/docs/announcements/current-release.md +++ b/docs/announcements/current-release.md @@ -1,4 +1,4 @@ -# MassGen v0.1.96 Release Announcement (OS-Level Agent Sandboxing) +# MassGen v0.1.97 Release Announcement (Application-Layer Permission Engine)