Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 815c28c8a3 |
@@ -0,0 +1,167 @@
|
||||
# `cleveragents.domain` — Domain Models
|
||||
|
||||
The `domain` package contains all domain-layer Pydantic models, value objects,
|
||||
and enumerations used throughout the CleverAgents platform. Domain models are
|
||||
pure data structures with no infrastructure dependencies — they may be freely
|
||||
imported by any layer.
|
||||
|
||||
See [ADR-001](../adr/ADR-001-layered-architecture.md) for the layered
|
||||
architecture rules that govern this package.
|
||||
|
||||
---
|
||||
|
||||
## Base Model
|
||||
|
||||
### `DomainBaseModel`
|
||||
|
||||
**Module:** `cleveragents.domain.models.base`
|
||||
|
||||
```python
|
||||
from cleveragents.domain.models.base import DomainBaseModel
|
||||
from pydantic import Field
|
||||
|
||||
class MyModel(DomainBaseModel):
|
||||
name: str
|
||||
count: int = Field(default=0, ge=0)
|
||||
```
|
||||
|
||||
Shared Pydantic base class for all standard domain-layer models. Inherit
|
||||
from `DomainBaseModel` instead of `pydantic.BaseModel` directly to ensure
|
||||
the shared `model_config` is applied consistently.
|
||||
|
||||
**`model_config` settings:**
|
||||
|
||||
| Setting | Value | Effect |
|
||||
|---------|-------|--------|
|
||||
| `str_strip_whitespace` | `True` | Leading/trailing whitespace is stripped from all `str` fields on assignment and validation |
|
||||
| `validate_assignment` | `True` | Field assignments after construction are validated just like constructor arguments |
|
||||
| `arbitrary_types_allowed` | `False` | All field types must be Pydantic-compatible; no raw Python objects |
|
||||
| `populate_by_name` | `True` | Models can be constructed using either the Python field name or the JSON alias |
|
||||
| `use_enum_values` | `True` | Enum fields are stored and serialised as their underlying primitive values |
|
||||
|
||||
**Why inherit from `DomainBaseModel`?**
|
||||
|
||||
Prior to its introduction, the same `model_config` block was duplicated
|
||||
across 14+ domain model files. `DomainBaseModel` centralises this
|
||||
configuration so that any future change propagates automatically to every
|
||||
consumer without a mass-edit.
|
||||
|
||||
**Example — whitespace stripping:**
|
||||
|
||||
```python
|
||||
from cleveragents.domain.models.base import DomainBaseModel
|
||||
|
||||
class PlanRecord(DomainBaseModel):
|
||||
title: str
|
||||
|
||||
record = PlanRecord(title=" My Plan ")
|
||||
assert record.title == "My Plan" # whitespace stripped automatically
|
||||
```
|
||||
|
||||
**Example — assignment validation:**
|
||||
|
||||
```python
|
||||
record.title = " Updated "
|
||||
assert record.title == "Updated" # validated and stripped on assignment
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core Domain Models
|
||||
|
||||
The following sub-packages contain the primary domain models. Each is
|
||||
documented in the relevant API or reference page.
|
||||
|
||||
| Sub-package | Description |
|
||||
|-------------|-------------|
|
||||
| `domain.models.core.plan` | `LifecyclePlan`, `PlanPhase`, `PlanState` |
|
||||
| `domain.models.core.session` | `Session`, `Message` |
|
||||
| `domain.models.core.actor` | `ActorConfig`, `ActorSpec` |
|
||||
| `domain.models.core.decision` | `Decision`, `DecisionType` |
|
||||
| `domain.models.core.resource` | `Resource`, `ResourceSpec` |
|
||||
| `domain.models.core.skill` | `Skill`, `SkillSpec` |
|
||||
| `domain.models.core.tool` | `Tool`, `ToolSpec` |
|
||||
| `domain.models.core.automation_profile` | `AutomationProfile` |
|
||||
| `domain.models.core.safety_profile` | `SafetyProfile` |
|
||||
| `domain.models.core.invariant` | `Invariant`, `InvariantResult` |
|
||||
| `domain.models.core.correction` | `CorrectionAttemptRecord`, `CorrectionAttemptState` |
|
||||
| `domain.models.core.estimation` | `EstimationResult` |
|
||||
| `domain.models.core.permission` | `PermissionRequest`, `PermissionDecision` |
|
||||
| `domain.models.core.inline_permission_question` | `InlinePermissionQuestion`, `PermissionDecisionEvent` |
|
||||
| `domain.models.thought.thought_block` | `ThoughtBlock` |
|
||||
| `domain.models.acms` | ACMS context fragment and tier models |
|
||||
|
||||
> **Note:** All models in `domain.models.core` and `domain.models.thought`
|
||||
> inherit from `DomainBaseModel` and therefore share the configuration
|
||||
> described above.
|
||||
|
||||
---
|
||||
|
||||
## ThoughtBlock
|
||||
|
||||
**Module:** `cleveragents.domain.models.thought.thought_block`
|
||||
|
||||
```python
|
||||
from cleveragents.domain.models.thought.thought_block import ThoughtBlock
|
||||
|
||||
block = ThoughtBlock(content="Reasoning trace...", max_lines=10)
|
||||
block.toggle() # expand / collapse
|
||||
block.is_expanded # bool
|
||||
block.truncated_content # first max_lines lines
|
||||
block.full_content # complete content
|
||||
```
|
||||
|
||||
Represents an actor reasoning trace rendered inline in the TUI conversation
|
||||
stream.
|
||||
|
||||
| Attribute / Method | Type | Description |
|
||||
|--------------------|------|-------------|
|
||||
| `content` | `str` | Full reasoning trace text |
|
||||
| `max_lines` | `int` | Maximum lines shown when collapsed (default: 10) |
|
||||
| `is_expanded` | `bool` | Whether the block is currently expanded |
|
||||
| `truncated_content` | `str` | First `max_lines` lines of `content` |
|
||||
| `full_content` | `str` | Alias for `content` |
|
||||
| `toggle()` | — | Toggle between expanded and collapsed |
|
||||
|
||||
---
|
||||
|
||||
## InlinePermissionQuestion
|
||||
|
||||
**Module:** `cleveragents.domain.models.core.inline_permission_question`
|
||||
|
||||
```python
|
||||
from cleveragents.domain.models.core.inline_permission_question import (
|
||||
InlinePermissionQuestion,
|
||||
PermissionDecision,
|
||||
PermissionDecisionEvent,
|
||||
)
|
||||
```
|
||||
|
||||
Value object representing a single-file permission request surfaced inline
|
||||
in the TUI conversation stream.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `tool_name` | `str` | Name of the tool requesting permission |
|
||||
| `file_path` | `str` | Path of the file being accessed |
|
||||
| `operation` | `str` | Requested operation (e.g. `"read"`, `"write"`) |
|
||||
| `diff` | `str \| None` | Optional unified diff of proposed changes |
|
||||
|
||||
**`PermissionDecision`** — enum of possible user decisions:
|
||||
|
||||
| Value | Meaning |
|
||||
|-------|---------|
|
||||
| `ALLOW_ONCE` | Allow this specific request |
|
||||
| `ALLOW_ALWAYS` | Allow all future requests from this tool in this session |
|
||||
| `REJECT_ONCE` | Reject this specific request |
|
||||
| `REJECT_ALWAYS` | Reject all future requests from this tool in this session |
|
||||
|
||||
**`PermissionDecisionEvent`** — emitted by `PermissionQuestionWidget` when
|
||||
the user resolves a permission request:
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class PermissionDecisionEvent:
|
||||
question: InlinePermissionQuestion
|
||||
decision: PermissionDecision
|
||||
```
|
||||
+2
-1
@@ -9,6 +9,7 @@ classes, functions, and exceptions with signatures and usage examples.
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| [`cleveragents.core`](core.md) | Exception hierarchy, error handling, retry patterns, async cleanup |
|
||||
| [`cleveragents.domain`](domain.md) | Domain base model, core domain models, value objects, and enumerations |
|
||||
| [`cleveragents.a2a`](a2a.md) | Agent-to-Agent (A2A) protocol facade, transport, models, and events |
|
||||
| [`cleveragents.actor`](actor.md) | Actor registry, configuration, loader, and compiler |
|
||||
| [`cleveragents.skills`](skills.md) | Skill framework — schema, protocol, registry, discovery, and inline executor |
|
||||
@@ -16,7 +17,7 @@ classes, functions, and exceptions with signatures and usage examples.
|
||||
| [`cleveragents.mcp`](mcp.md) | Model Context Protocol (MCP) adapter, client, registry, and sandbox |
|
||||
| [`cleveragents.resource`](resource.md) | Resource schema, handlers, and type-inheritance system |
|
||||
| [`cleveragents.config`](config.md) | Application settings, logging, metrics, and security scanning |
|
||||
| [`cleveragents.tui`](tui.md) | Interactive Terminal UI — app, persona system, input routing, slash commands, session export/import |
|
||||
| [`cleveragents.tui`](tui.md) | Interactive Terminal UI — app, persona system, input routing, slash commands, shell safety, session export/import |
|
||||
|
||||
> **Note:** Internal modules (prefixed with `_`) and implementation details
|
||||
> not listed in a module's `__all__` are considered private and may change
|
||||
|
||||
+153
@@ -269,6 +269,159 @@ from cleveragents.tui.screens import PermissionsScreen
|
||||
|
||||
---
|
||||
|
||||
## Shell Safety
|
||||
|
||||
**Module:** `cleveragents.tui.shell_safety`
|
||||
|
||||
The shell safety subsystem detects dangerous command patterns before the TUI
|
||||
executes them in shell mode (`!` prefix). A configurable pattern registry
|
||||
classifies commands by danger level and surfaces a warning overlay before
|
||||
proceeding.
|
||||
|
||||
### `ShellDangerLevel`
|
||||
|
||||
```python
|
||||
from cleveragents.tui.shell_safety import ShellDangerLevel
|
||||
```
|
||||
|
||||
`IntEnum` with four ordered severity levels:
|
||||
|
||||
| Level | Value | Description |
|
||||
|-------|-------|-------------|
|
||||
| `LOW` | 1 | Minor risk — generally recoverable (e.g. `chmod 777` on a single file) |
|
||||
| `MEDIUM` | 2 | Moderate risk — can cause data loss or security exposure (e.g. `sudo rm`, `wget \| sh`) |
|
||||
| `HIGH` | 3 | High risk — significant, hard-to-reverse damage (e.g. `dd if=`, `mkfs`) |
|
||||
| `CRITICAL` | 4 | Critical risk — can destroy the entire system (e.g. `rm -rf /`, fork bomb) |
|
||||
|
||||
Levels are ordered so numeric comparisons work naturally:
|
||||
`level >= ShellDangerLevel.HIGH`.
|
||||
|
||||
---
|
||||
|
||||
### `DangerousPattern`
|
||||
|
||||
```python
|
||||
from cleveragents.tui.shell_safety import DangerousPattern
|
||||
|
||||
pattern = DangerousPattern(
|
||||
name="rm_rf_root",
|
||||
pattern=r"rm\s+(-\w*r\w*f|-\w*f\w*r)\s+/\s*$",
|
||||
level=ShellDangerLevel.CRITICAL,
|
||||
description="rm -rf / recursively deletes the entire filesystem root.",
|
||||
)
|
||||
pattern.matches("rm -rf /") # True
|
||||
```
|
||||
|
||||
Immutable frozen dataclass describing a single dangerous shell pattern.
|
||||
|
||||
| Attribute | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `name` | `str` | Short human-readable identifier |
|
||||
| `pattern` | `str` | Regular expression matched against the command |
|
||||
| `level` | `ShellDangerLevel` | Severity classification |
|
||||
| `description` | `str` | Human-readable explanation of the risk |
|
||||
| `case_sensitive` | `bool` | When `True` the regex is case-sensitive (default: `False`) |
|
||||
|
||||
**Method:** `matches(command: str) → bool` — returns `True` if the command
|
||||
matches this pattern.
|
||||
|
||||
---
|
||||
|
||||
### `DangerousPatternDetector`
|
||||
|
||||
```python
|
||||
from cleveragents.tui.shell_safety import DangerousPatternDetector
|
||||
|
||||
detector = DangerousPatternDetector()
|
||||
warnings = detector.check("rm -rf /")
|
||||
first = detector.check_first("sudo rm important.txt")
|
||||
```
|
||||
|
||||
Checks shell commands against a configurable registry of dangerous patterns.
|
||||
Ships with `DEFAULT_PATTERNS` but supports full customisation.
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `check(command) → list[DangerousCommandWarning]` | Return all matching warnings |
|
||||
| `check_first(command) → DangerousCommandWarning \| None` | Return the first (highest-priority) warning |
|
||||
| `is_dangerous(command) → bool` | Return `True` if any pattern matches |
|
||||
| `max_danger_level(command) → ShellDangerLevel \| None` | Return the highest matched danger level |
|
||||
| `add_pattern(pattern)` | Append a pattern to the registry |
|
||||
| `remove_pattern(name) → bool` | Remove a pattern by name; returns `True` if found |
|
||||
| `replace_patterns(patterns)` | Replace the entire registry |
|
||||
| `patterns` | Read-only tuple of current patterns |
|
||||
|
||||
**Built-in patterns** (`DEFAULT_PATTERNS`):
|
||||
|
||||
| Name | Level | Matches |
|
||||
|------|-------|---------|
|
||||
| `rm_rf_root` | CRITICAL | `rm -rf /` |
|
||||
| `rm_rf_wildcard` | CRITICAL | `rm -rf /*` or `rm -rf /path*` |
|
||||
| `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` |
|
||||
|
||||
---
|
||||
|
||||
### `ShellSafetyService`
|
||||
|
||||
```python
|
||||
from cleveragents.tui.shell_safety import ShellSafetyService
|
||||
|
||||
service = ShellSafetyService()
|
||||
result = service.check_command("rm -rf /")
|
||||
if not result.allowed:
|
||||
print(result.warning.message)
|
||||
```
|
||||
|
||||
Application service that wraps `DangerousPatternDetector` with a higher-level
|
||||
API suitable for use by the TUI.
|
||||
|
||||
```python
|
||||
ShellSafetyService(
|
||||
detector=None, # custom DangerousPatternDetector (optional)
|
||||
block_level=ShellDangerLevel.MEDIUM, # auto-block threshold
|
||||
warn_callback=None, # (warning) -> bool; True = allow, False = block
|
||||
extra_patterns=None, # additional DangerousPattern objects
|
||||
)
|
||||
```
|
||||
|
||||
| Method / Property | Description |
|
||||
|-------------------|-------------|
|
||||
| `check_command(command) → SafetyCheckResult` | Full safety check with allow/block decision |
|
||||
| `is_safe(command) → bool` | Convenience wrapper; `True` if allowed |
|
||||
| `detector` | The underlying `DangerousPatternDetector` |
|
||||
| `block_level` | The minimum level that triggers automatic blocking |
|
||||
|
||||
**`SafetyCheckResult`** attributes:
|
||||
|
||||
| Attribute | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `command` | `str` | The command that was checked |
|
||||
| `warning` | `DangerousCommandWarning \| None` | Warning if a pattern matched |
|
||||
| `allowed` | `bool` | `True` if the command may proceed |
|
||||
|
||||
**Custom warn callback example:**
|
||||
|
||||
```python
|
||||
def my_callback(warning: DangerousCommandWarning) -> bool:
|
||||
# Show a UI prompt; return True to allow, False to block
|
||||
return show_confirmation_dialog(warning.message)
|
||||
|
||||
service = ShellSafetyService(warn_callback=my_callback)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TUI State Persistence
|
||||
|
||||
The TUI persists minimal state to `~/.config/cleveragents/tui-state.yaml`:
|
||||
|
||||
@@ -14,6 +14,7 @@ nav:
|
||||
- API Reference:
|
||||
- Overview: api/index.md
|
||||
- Core Utilities: api/core.md
|
||||
- Domain Models: api/domain.md
|
||||
- A2A Protocol: api/a2a.md
|
||||
- Actor System: api/actor.md
|
||||
- Skills: api/skills.md
|
||||
|
||||
Reference in New Issue
Block a user