docs: shell safety API, invariant reconciliation architecture, new feature highlights #3476
+71
-3
@@ -293,7 +293,8 @@ from cleveragents.tui.shell_safety import ShellDangerLevel
|
||||
| `HIGH` | 3 | High risk — significant, hard-to-reverse damage (e.g. `dd if=`, `mkfs`) |
|
||||
| `CRITICAL` | 4 | Critical risk — can destroy the system or create a fork bomb (e.g. `rm -rf /`, `:(){ :\|:& };:`) |
|
||||
|
||||
Numeric comparisons work naturally: `level >= ShellDangerLevel.HIGH`.
|
||||
Because `ShellDangerLevel` is an `IntEnum`, numeric comparisons work
|
||||
naturally: `level >= ShellDangerLevel.HIGH`.
|
||||
|
||||
---
|
||||
|
||||
@@ -302,7 +303,6 @@ Numeric comparisons work naturally: `level >= ShellDangerLevel.HIGH`.
|
||||
```python
|
||||
from cleveragents.tui.shell_safety import DangerousPattern
|
||||
|
||||
pattern = DangerousPattern(
|
||||
name="rm_rf_root",
|
||||
pattern=r"rm\s+-[rRfF]*\s+/",
|
||||
level=ShellDangerLevel.CRITICAL,
|
||||
@@ -324,6 +324,54 @@ Immutable frozen dataclass describing a single dangerous shell pattern.
|
||||
|
||||
---
|
||||
|
||||
### Built-in Patterns (`DEFAULT_PATTERNS`)
|
||||
|
||||
```python
|
||||
from cleveragents.tui.shell_safety.pattern_registry import DEFAULT_PATTERNS
|
||||
```
|
||||
|
||||
The default pattern set covers the most common dangerous shell operations:
|
||||
|
||||
| Name | Level | Trigger |
|
||||
|------|-------|---------|
|
||||
| `rm_rf_root` | CRITICAL | `rm -rf /` |
|
||||
| `rm_rf_wildcard` | CRITICAL | `rm -rf /*` or similar |
|
||||
| `fork_bomb` | CRITICAL | `:(){ :|:& };:` |
|
||||
| `dd_if_device` | HIGH | `dd if=` |
|
||||
| `mkfs` | HIGH | `mkfs` |
|
||||
| `shred_device` | HIGH | `shred /dev/` or `shred --remove` |
|
||||
| `chmod_777` | MEDIUM | `chmod 777` |
|
||||
| `sudo_rm` | MEDIUM | `sudo rm` |
|
||||
| `wget_pipe_sh` | MEDIUM | `wget … \| sh` |
|
||||
| `curl_pipe_sh` | MEDIUM | `curl … \| sh` |
|
||||
| `wget_pipe_bash` | MEDIUM | `wget … \| bash` |
|
||||
| `curl_pipe_bash` | MEDIUM | `curl … \| bash` |
|
||||
| `git_push_force` | LOW | `git push --force` |
|
||||
| `chmod_recursive_permissive` | LOW | `chmod -R 6xx/7xx` |
|
||||
|
||||
---
|
||||
|
||||
### `DangerousPatternDetector`
|
||||
|
||||
```python
|
||||
from cleveragents.tui.shell_safety import DangerousPatternDetector
|
||||
|
||||
detector = DangerousPatternDetector()
|
||||
warning = detector.check_first("rm -rf /")
|
||||
# → DangerousCommandWarning or None
|
||||
```
|
||||
|
||||
Checks a command string against all registered patterns and returns the
|
||||
first match as a `DangerousCommandWarning`, or `None` if the command is safe.
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `check_first(command) → DangerousCommandWarning \| None` | Return the first matching warning |
|
||||
| `check_all(command) → list[DangerousCommandWarning]` | Return all matching warnings |
|
||||
| `add_pattern(pattern)` | Register an additional `DangerousPattern` |
|
||||
|
||||
---
|
||||
|
||||
### `ShellSafetyService`
|
||||
|
||||
```python
|
||||
@@ -353,7 +401,11 @@ Application service that checks shell commands before execution.
|
||||
| `check_command(command)` | `SafetyCheckResult` | Check a command and return a structured result |
|
||||
| `is_safe(command)` | `bool` | Convenience wrapper — returns `True` if the command passes |
|
||||
|
||||
**`SafetyCheckResult`** attributes:
|
||||
**`SafetyCheckResult`** — import path and attributes:
|
||||
|
||||
```python
|
||||
from cleveragents.tui.shell_safety.safety_service import SafetyCheckResult
|
||||
```
|
||||
|
||||
| Attribute | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
@@ -361,6 +413,22 @@ Application service that checks shell commands before execution.
|
||||
| `warning` | `DangerousCommandWarning \| None` | Warning if a dangerous pattern matched, otherwise `None` |
|
||||
| `allowed` | `bool` | `True` if the command is allowed to proceed |
|
||||
|
||||
**Example — custom block level and callback:**
|
||||
|
||||
```python
|
||||
from cleveragents.tui.shell_safety import ShellSafetyService, ShellDangerLevel
|
||||
|
||||
def my_callback(warning):
|
||||
# Show a UI prompt; return True to allow, False to block
|
||||
return ask_user(f"⚠️ {warning.message}. Proceed?")
|
||||
|
||||
service = ShellSafetyService(
|
||||
block_level=ShellDangerLevel.HIGH,
|
||||
warn_callback=my_callback,
|
||||
)
|
||||
result = service.check_command("sudo rm /etc/hosts")
|
||||
```
|
||||
|
||||
**Built-in pattern categories:**
|
||||
|
||||
- Destructive filesystem operations (`rm -rf`, `shred`, `wipe`)
|
||||
|
||||
@@ -219,6 +219,46 @@ Key UKO capabilities:
|
||||
|
||||
---
|
||||
|
||||
## Invariant Reconciliation
|
||||
|
||||
The **Invariant Reconciliation Actor** (`builtin/invariant-reconciliation`)
|
||||
is automatically invoked at the start of the Strategize, Execute, and Apply
|
||||
phase transitions by `PlanLifecycleService`.
|
||||
|
||||
**Reconciliation algorithm** (spec §19440–19600):
|
||||
|
||||
1. Collect invariants from four scopes: global, project, action, plan.
|
||||
2. Group by normalised text (case-insensitive, stripped).
|
||||
3. Detect conflicts between invariants at different scopes.
|
||||
4. Resolve using specificity: `plan > action > project > global`.
|
||||
Exception: `non_overridable` global invariants always win.
|
||||
5. Record an `invariant_enforced` decision for each active invariant.
|
||||
6. Return a reconciled `InvariantSet`.
|
||||
|
||||
**Failure behaviour:**
|
||||
|
||||
- Reconciliation failures block the phase transition with
|
||||
`ReconciliationBlockedError` and emit an `INVARIANT_VIOLATED` event.
|
||||
- Post-correction reconciliation runs via `CORRECTION_APPLIED` event
|
||||
subscription (best-effort; does not block correction completion).
|
||||
|
||||
**DI registration:** `InvariantService` is registered as a Singleton
|
||||
provider in the DI container.
|
||||
|
||||
```python
|
||||
from cleveragents.actor.reconciliation import InvariantReconciliationActor
|
||||
|
||||
actor = InvariantReconciliationActor(
|
||||
invariant_service=container.invariant_service(),
|
||||
decision_service=container.decision_service(),
|
||||
)
|
||||
result = actor.run(plan_id="...", project_name="...", action_name="...")
|
||||
# result.reconciled_set — effective InvariantSet
|
||||
# result.conflicts — list[ConflictRecord] with resolution details
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## A2A Protocol Boundary
|
||||
|
||||
All CLI, TUI, and server interactions cross the A2A boundary
|
||||
|
||||
Reference in New Issue
Block a user