Skip to content
8 changes: 8 additions & 0 deletions .cspell-repo-terms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,11 @@ urlopen
davidequarracino
Marp
Slidev
classmethod
Docstrings
isort
mypy
Pydantic
pytest
pyupgrade
xfail
14 changes: 14 additions & 0 deletions agent-governance-python/agent-os/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ ruff format .
- Event types: `GovernanceEventType.POLICY_CHECK`, `.POLICY_VIOLATION`, `.TOOL_CALL_BLOCKED`, `.CHECKPOINT_CREATED`
- Tests go in `tests/` (unit) or `modules/*/tests/` (module-specific)

## Policy Check Result (additive)

Policy checks should prefer the additive `*_execute_check` methods when new code needs structured denial data, while preserving the legacy tuple-returning methods for compatibility. Raise the canonical `PolicyViolationError.from_check_result(result)` for new typed flows; hosts should surface `str(e)` and use `e.check_result.category` for dispatch instead of substring-matching internal details.

```python
from agent_os.policies.decision import ViolationCategory
from agent_os.exceptions import PolicyViolationError

result = kernel.pre_execute_check(ctx, input_data)
if not result.allowed:
raise PolicyViolationError.from_check_result(result)
# Hosts: surface str(e); switch on e.check_result.category for typed dispatch.
```

## Boundaries

- **Never modify** `tests/test_mcp_server.py` (known pre-existing failure, excluded from CI)
Expand Down
24 changes: 24 additions & 0 deletions agent-governance-python/agent-os/src/agent_os/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
for structured error handling and logging.
"""

from __future__ import annotations

from datetime import datetime, timezone
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from agent_os.policies.decision import PolicyCheckResult


class AgentOSError(Exception):
Expand Down Expand Up @@ -43,6 +49,24 @@ class PolicyViolationError(PolicyError):

def __init__(self, message, error_code=None, details=None):
super().__init__(message, error_code or "POLICY_VIOLATION", details)
self.check_result = None

@classmethod
def from_check_result(cls, result: PolicyCheckResult) -> PolicyViolationError:
"""Create a policy violation error from a structured check result."""

details = {
"category": result.category.value if result.category else None,
"matched_rule": result.matched_rule,
"detail": result.detail,
"scope": result.scope,
"operation": result.operation,
"tool_name": result.tool_name,
**result.audit_entry,
}
e = cls(result.public_message, "POLICY_VIOLATION", details)
e.check_result = result
return e


class PolicyDeniedError(PolicyError):
Expand Down
Loading
Loading