- Promote [Unreleased] CHANGELOG entries to [3.8.0] (2026-04-05) - Add Shell Danger Detection section to docs/api/tui.md covering ShellDangerLevel, DangerousPattern, ShellSafetyService, and SafetyCheckResult with full API reference and usage examples - Add InvariantService section to docs/api/core.md documenting the new DI-registered singleton, its methods, and emitted events - Update docs/architecture.md Plan Lifecycle section to document invariant reconciliation as a phase transition gate - Update README.md Highlights with shell danger detection, inline permission questions, invariant reconciliation, UKO provenance tracking, and JSON-RPC 2.0 A2A wire format - Create docs/modules/shell-safety.md with full module guide covering purpose, key classes, built-in patterns, custom pattern registration, TUI integration, and testing guidance ISSUES CLOSED: #1003 #997 #1391 #1004 #891 #1501 #1577 #1941 #2334
8.1 KiB
cleveragents.core — Core Utilities
The core package provides the exception hierarchy, error classification,
retry patterns, circuit breaker, and async resource cleanup used throughout
the entire CleverAgents platform.
Domain Base Model
Module: cleveragents.domain.models.base
DomainBaseModel
from cleveragents.domain.models.base import DomainBaseModel
class DomainBaseModel(BaseModel):
model_config = ConfigDict(
str_strip_whitespace=True,
validate_assignment=True,
arbitrary_types_allowed=False,
populate_by_name=True,
use_enum_values=True,
)
Shared Pydantic base class for all standard domain-layer models. Inherit
from DomainBaseModel instead of pydantic.BaseModel directly to get the
canonical domain-layer configuration in one place.
Configuration semantics:
| Setting | Effect |
|---|---|
str_strip_whitespace |
Leading/trailing whitespace stripped from all str fields on assignment and validation |
validate_assignment |
Field assignments after construction are validated like constructor arguments |
arbitrary_types_allowed=False |
All field types must be Pydantic-compatible — keeps the domain layer clean |
populate_by_name |
Models can be constructed using either the Python field name or the JSON alias |
use_enum_values |
Enum fields are stored and serialised as their underlying primitive values |
Usage:
from cleveragents.domain.models.base import DomainBaseModel
from pydantic import Field
class MyDomainModel(DomainBaseModel):
name: str
count: int = Field(ge=0)
Note: This class was introduced in v3.7.0 (PR #1941) to eliminate the duplicated
model_configthat previously appeared in 14 separate domain model files. It is a pure structural refactor with no behavioral changes.
Exception Hierarchy
All exceptions inherit from CleverAgentsError. Catch the most specific
type you can handle; let everything else propagate.
CleverAgentsError
├── DomainError
│ ├── ValidationError
│ ├── BusinessRuleViolation
│ │ ├── LockConflictError
│ │ └── DecisionPhaseViolationError
│ ├── ResourceNotFoundError (alias: NotFoundError)
│ ├── ResourceConflictError
│ ├── LockExpiredError
│ └── PlanError
├── InfrastructureError
│ ├── DatabaseError
│ │ └── MigrationNotApprovedError
│ ├── NetworkError
│ └── ExternalServiceError
├── ProviderError
│ ├── RateLimitError
│ ├── ModelNotAvailableError
│ └── TokenLimitExceededError
├── AuthenticationError
├── AuthorizationError
├── ConfigurationError
│ └── MissingConfigurationError
├── FileSystemError
├── ExecutionError
├── StreamRoutingError
└── UnsafeConfigurationError
CleverAgentsError
class CleverAgentsError(Exception):
message: str
details: dict[str, Any]
Base class for all platform exceptions. Always carries a human-readable
message and an optional details dict for structured context.
ResourceNotFoundError
class ResourceNotFoundError(DomainError):
resource_type: str | None
resource_id: str | None
Raised when a requested entity does not exist. If resource_type and
resource_id are provided the message is auto-generated.
from cleveragents.core.exceptions import ResourceNotFoundError
raise ResourceNotFoundError(resource_type="plan", resource_id="plan-42")
# → "Plan 'plan-42' not found"
LockConflictError
class LockConflictError(BusinessRuleViolation):
resource_type: str
resource_id: str
owner_id: str
Raised when an advisory lock cannot be acquired because another owner holds it.
RateLimitError
class RateLimitError(ProviderError):
retry_after: int | None # seconds to wait before retrying
DecisionPhaseViolationError
class DecisionPhaseViolationError(BusinessRuleViolation):
decision_type: str
plan_phase: str
allowed_types: frozenset[str]
Raised when a decision type is incompatible with the plan's current phase.
Error Handling Utilities
Module: cleveragents.core.error_handling
ErrorCode
IntEnum mapping exception categories to HTTP-like numeric codes used for
consistent CLI and API output.
classify_error(exc) → ErrorCode
Returns the ErrorCode for any exception, falling back to
ErrorCode.INTERNAL for unknown types.
redact_error_details(details) → dict
Strips sensitive keys (tokens, passwords, secrets) from an error details dict before logging.
wrap_unexpected(exc, *, context=None) → CleverAgentsError
Wraps an unexpected exception in a CleverAgentsError with a safe
user-facing message that hides internal stack details.
from cleveragents.core.error_handling import wrap_unexpected
try:
risky_operation()
except Exception as exc:
safe = wrap_unexpected(exc, context={"plan_id": plan_id})
raise safe from exc
Retry Patterns
Module: cleveragents.core.retry_patterns
Provides decorators and helpers for exponential-backoff retry with jitter, configurable per exception type.
Async Cleanup
Module: cleveragents.core.async_cleanup
AsyncResourceTracker — unified async resource lifecycle with
timeout-bounded cleanup, leak detection via finalizer, and async context
manager support.
async with AsyncResourceTracker() as tracker:
conn = await tracker.register(open_connection())
# conn is automatically closed on exit, even on error
Circuit Breaker
Module: cleveragents.core.circuit_breaker
Implements the circuit-breaker pattern to prevent cascading failures when calling external services.
Invariant Service
Module: cleveragents.application.services.invariant_service
The InvariantService manages invariant constraints across scopes and is
automatically invoked at every plan phase transition by the
InvariantReconciliationActor.
InvariantService
from cleveragents.application.services.invariant_service import InvariantService
from cleveragents.domain.models.core.invariant import InvariantScope
service = InvariantService(event_bus=event_bus)
inv = service.add_invariant(
text="All output files must be UTF-8 encoded",
scope=InvariantScope.PROJECT,
source_name="my-project",
)
Constructor parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
event_bus |
EventBus | None |
None |
Optional event bus for emitting INVARIANT_VIOLATED / INVARIANT_ENFORCED / INVARIANT_RECONCILED events |
Methods:
| Method | Returns | Description |
|---|---|---|
add_invariant(text, scope, source_name) |
Invariant |
Add a new invariant; validates and sanitizes text |
list_invariants(scope, source_name, effective) |
list[Invariant] |
Filter invariants; pass effective=True to get the merged precedence chain |
remove_invariant(invariant_id) |
Invariant |
Soft-delete an invariant (sets active=False) |
get_effective_invariants(plan_id, project_name) |
list[Invariant] |
Return merged invariants using plan > project > global precedence |
enforce_invariants(plan_id, invariants, actor_response, violated_invariant_ids) |
list[InvariantEnforcementRecord] |
Create enforcement records; emits INVARIANT_VIOLATED for each violation |
Merge precedence: plan > project > global. Duplicate invariant texts are
de-duplicated by merge_invariants().
Events emitted:
| Event | When |
|---|---|
INVARIANT_VIOLATED |
An invariant in violated_invariant_ids was not satisfied |
INVARIANT_ENFORCED |
Each invariant enforcement record is created |
INVARIANT_RECONCILED |
Once per enforce_invariants() call (batch summary) |
Note: The
InvariantServiceis registered as a Singleton provider in the DI container. Obtain it viacontainer.invariant_service()rather than constructing it directly.