coreason-manifest 0.6.0__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {coreason_manifest-0.6.0 → coreason_manifest-0.7.0}/PKG-INFO +1 -7
- {coreason_manifest-0.6.0 → coreason_manifest-0.7.0}/pyproject.toml +3 -9
- coreason_manifest-0.7.0/src/coreason_manifest/__init__.py +17 -0
- coreason_manifest-0.7.0/src/coreason_manifest/definitions/__init__.py +0 -0
- coreason_manifest-0.7.0/src/coreason_manifest/definitions/audit.py +7 -0
- coreason_manifest-0.7.0/src/coreason_manifest/definitions/simulation.py +19 -0
- coreason_manifest-0.6.0/src/coreason_manifest/recipes.py → coreason_manifest-0.7.0/src/coreason_manifest/definitions/topology.py +2 -23
- coreason_manifest-0.7.0/src/coreason_manifest/recipes.py +38 -0
- coreason_manifest-0.6.0/src/coreason_manifest/__init__.py +0 -79
- coreason_manifest-0.6.0/src/coreason_manifest/engine.py +0 -222
- coreason_manifest-0.6.0/src/coreason_manifest/errors.py +0 -53
- coreason_manifest-0.6.0/src/coreason_manifest/integrity.py +0 -141
- coreason_manifest-0.6.0/src/coreason_manifest/loader.py +0 -271
- coreason_manifest-0.6.0/src/coreason_manifest/main.py +0 -17
- coreason_manifest-0.6.0/src/coreason_manifest/policies/compliance.rego +0 -81
- coreason_manifest-0.6.0/src/coreason_manifest/policies/tbom.json +0 -14
- coreason_manifest-0.6.0/src/coreason_manifest/policy.py +0 -138
- coreason_manifest-0.6.0/src/coreason_manifest/server.py +0 -123
- coreason_manifest-0.6.0/src/coreason_manifest/validator.py +0 -67
- {coreason_manifest-0.6.0 → coreason_manifest-0.7.0}/LICENSE +0 -0
- {coreason_manifest-0.6.0 → coreason_manifest-0.7.0}/NOTICE +0 -0
- {coreason_manifest-0.6.0 → coreason_manifest-0.7.0}/README.md +0 -0
- /coreason_manifest-0.6.0/src/coreason_manifest/models.py → /coreason_manifest-0.7.0/src/coreason_manifest/definitions/agent.py +0 -0
- {coreason_manifest-0.6.0 → coreason_manifest-0.7.0}/src/coreason_manifest/schemas/__init__.py +0 -0
- {coreason_manifest-0.6.0 → coreason_manifest-0.7.0}/src/coreason_manifest/schemas/agent.schema.json +0 -0
- {coreason_manifest-0.6.0 → coreason_manifest-0.7.0}/src/coreason_manifest/utils/__init__.py +0 -0
- {coreason_manifest-0.6.0 → coreason_manifest-0.7.0}/src/coreason_manifest/utils/logger.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coreason_manifest
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: This package is the definitive source of truth. If it isn't in the manifest, it doesn't exist. If it violates the manifest, it doesn't run.
|
|
5
5
|
License: # The Prosperity Public License 3.0.0
|
|
6
6
|
|
|
@@ -67,16 +67,10 @@ Requires-Python: >=3.12
|
|
|
67
67
|
Classifier: License :: Other/Proprietary License
|
|
68
68
|
Classifier: Programming Language :: Python :: 3.12
|
|
69
69
|
Classifier: Operating System :: OS Independent
|
|
70
|
-
Requires-Dist: aiofiles (>=23.2.1,<24.0.0)
|
|
71
|
-
Requires-Dist: anyio (>=4.12.1,<5.0.0)
|
|
72
70
|
Requires-Dist: coreason-identity (>=0.4.1,<0.5.0)
|
|
73
|
-
Requires-Dist: fastapi (>=0.128.0,<0.129.0)
|
|
74
|
-
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
|
75
|
-
Requires-Dist: jsonschema (>=4.26.0,<5.0.0)
|
|
76
71
|
Requires-Dist: loguru (>=0.7.2,<0.8.0)
|
|
77
72
|
Requires-Dist: pydantic (>=2.12.5,<3.0.0)
|
|
78
73
|
Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
|
|
79
|
-
Requires-Dist: uvicorn (>=0.40.0,<0.41.0)
|
|
80
74
|
Project-URL: Documentation, https://github.com/CoReason-AI/coreason_manifest
|
|
81
75
|
Project-URL: Homepage, https://github.com/CoReason-AI/coreason_manifest
|
|
82
76
|
Project-URL: Repository, https://github.com/CoReason-AI/coreason_manifest
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "coreason_manifest"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.7.0"
|
|
4
4
|
description = "This package is the definitive source of truth. If it isn't in the manifest, it doesn't exist. If it violates the manifest, it doesn't run."
|
|
5
5
|
authors = ["Gowtham A Rao <gowtham.rao@coreason.ai>"]
|
|
6
6
|
license = "Prosperity-3.0"
|
|
@@ -11,14 +11,8 @@ packages = [{include = "coreason_manifest", from = "src"}]
|
|
|
11
11
|
python = ">=3.12, <3.15"
|
|
12
12
|
loguru = "^0.7.2"
|
|
13
13
|
pydantic = "^2.12.5"
|
|
14
|
-
jsonschema = "^4.26.0"
|
|
15
14
|
pyyaml = "^6.0.3"
|
|
16
|
-
anyio = "^4.12.1"
|
|
17
15
|
coreason-identity = "^0.4.1"
|
|
18
|
-
httpx = "^0.28.1"
|
|
19
|
-
aiofiles = "^23.2.1"
|
|
20
|
-
fastapi = "^0.128.0"
|
|
21
|
-
uvicorn = "^0.40.0"
|
|
22
16
|
|
|
23
17
|
[tool.poetry.group.dev.dependencies]
|
|
24
18
|
pytest = "^9.0.2"
|
|
@@ -29,8 +23,8 @@ mkdocs = "^1.6.1"
|
|
|
29
23
|
mkdocs-material = "^9.7.1"
|
|
30
24
|
pydantic = "^2.12.5"
|
|
31
25
|
mypy = "^1.19.1"
|
|
32
|
-
types-aiofiles = "^23.2.0"
|
|
33
26
|
pytest-asyncio = "^1.3.0"
|
|
27
|
+
types-pyyaml = "^6.0.12.20250915"
|
|
34
28
|
|
|
35
29
|
[build-system]
|
|
36
30
|
requires = ["poetry-core"]
|
|
@@ -38,7 +32,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
38
32
|
|
|
39
33
|
[project]
|
|
40
34
|
name = "coreason_manifest"
|
|
41
|
-
version = "0.
|
|
35
|
+
version = "0.7.0"
|
|
42
36
|
description = "This package is the definitive source of truth. If it isn't in the manifest, it doesn't exist. If it violates the manifest, it doesn't run."
|
|
43
37
|
readme = "README.md"
|
|
44
38
|
requires-python = ">=3.12"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .definitions.agent import AgentDefinition
|
|
2
|
+
from .definitions.audit import AuditLog
|
|
3
|
+
from .definitions.simulation import SimulationScenario, SimulationTrace, SimulationTurn
|
|
4
|
+
from .definitions.topology import Edge, Node, Topology
|
|
5
|
+
from .recipes import RecipeManifest
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"AgentDefinition",
|
|
9
|
+
"Topology",
|
|
10
|
+
"Node",
|
|
11
|
+
"Edge",
|
|
12
|
+
"SimulationScenario",
|
|
13
|
+
"SimulationTrace",
|
|
14
|
+
"SimulationTurn",
|
|
15
|
+
"AuditLog",
|
|
16
|
+
"RecipeManifest",
|
|
17
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SimulationScenario(BaseModel):
|
|
5
|
+
"""Definition of a simulation scenario."""
|
|
6
|
+
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SimulationTrace(BaseModel):
|
|
11
|
+
"""Trace of a simulation execution."""
|
|
12
|
+
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SimulationTurn(BaseModel):
|
|
17
|
+
"""A single turn in a simulation."""
|
|
18
|
+
|
|
19
|
+
pass
|
|
@@ -8,12 +8,10 @@
|
|
|
8
8
|
#
|
|
9
9
|
# Source Code: https://github.com/CoReason-AI/coreason_maco
|
|
10
10
|
|
|
11
|
-
from typing import Annotated,
|
|
11
|
+
from typing import Annotated, List, Literal, Optional, Union
|
|
12
12
|
|
|
13
13
|
from pydantic import BaseModel, ConfigDict, Field
|
|
14
14
|
|
|
15
|
-
from .models import VersionStr
|
|
16
|
-
|
|
17
15
|
|
|
18
16
|
class CouncilConfig(BaseModel):
|
|
19
17
|
"""Configuration for 'Architectural Triangulation'.
|
|
@@ -139,23 +137,4 @@ class GraphTopology(BaseModel):
|
|
|
139
137
|
edges: List[Edge] = Field(..., description="List of edges connecting the nodes.")
|
|
140
138
|
|
|
141
139
|
|
|
142
|
-
|
|
143
|
-
"""The executable specification for the MACO engine.
|
|
144
|
-
|
|
145
|
-
Attributes:
|
|
146
|
-
id: Unique identifier for the recipe.
|
|
147
|
-
version: Version of the recipe.
|
|
148
|
-
name: Human-readable name of the recipe.
|
|
149
|
-
description: Detailed description of the recipe.
|
|
150
|
-
inputs: Schema defining global variables this recipe accepts.
|
|
151
|
-
graph: The topology definition of the workflow.
|
|
152
|
-
"""
|
|
153
|
-
|
|
154
|
-
model_config = ConfigDict(extra="forbid")
|
|
155
|
-
|
|
156
|
-
id: str = Field(..., description="Unique identifier for the recipe.")
|
|
157
|
-
version: VersionStr = Field(..., description="Version of the recipe.")
|
|
158
|
-
name: str = Field(..., description="Human-readable name of the recipe.")
|
|
159
|
-
description: Optional[str] = Field(None, description="Detailed description of the recipe.")
|
|
160
|
-
inputs: Dict[str, Any] = Field(..., description="Schema defining global variables this recipe accepts.")
|
|
161
|
-
graph: GraphTopology = Field(..., description="The topology definition of the workflow.")
|
|
140
|
+
Topology = GraphTopology
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Copyright (c) 2025 CoReason, Inc.
|
|
2
|
+
#
|
|
3
|
+
# This software is proprietary and dual-licensed.
|
|
4
|
+
# Licensed under the Prosperity Public License 3.0 (the "License").
|
|
5
|
+
# A copy of the license is available at https://prosperitylicense.com/versions/3.0.0
|
|
6
|
+
# For details, see the LICENSE file.
|
|
7
|
+
# Commercial use beyond a 30-day trial requires a separate license.
|
|
8
|
+
#
|
|
9
|
+
# Source Code: https://github.com/CoReason-AI/coreason_maco
|
|
10
|
+
|
|
11
|
+
from typing import Any, Dict, Optional
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
14
|
+
|
|
15
|
+
from .definitions.agent import VersionStr
|
|
16
|
+
from .definitions.topology import GraphTopology
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RecipeManifest(BaseModel):
|
|
20
|
+
"""The executable specification for the MACO engine.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
id: Unique identifier for the recipe.
|
|
24
|
+
version: Version of the recipe.
|
|
25
|
+
name: Human-readable name of the recipe.
|
|
26
|
+
description: Detailed description of the recipe.
|
|
27
|
+
inputs: Schema defining global variables this recipe accepts.
|
|
28
|
+
graph: The topology definition of the workflow.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
model_config = ConfigDict(extra="forbid")
|
|
32
|
+
|
|
33
|
+
id: str = Field(..., description="Unique identifier for the recipe.")
|
|
34
|
+
version: VersionStr = Field(..., description="Version of the recipe.")
|
|
35
|
+
name: str = Field(..., description="Human-readable name of the recipe.")
|
|
36
|
+
description: Optional[str] = Field(None, description="Detailed description of the recipe.")
|
|
37
|
+
inputs: Dict[str, Any] = Field(..., description="Schema defining global variables this recipe accepts.")
|
|
38
|
+
graph: GraphTopology = Field(..., description="The topology definition of the workflow.")
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# Prosperity-3.0
|
|
2
|
-
"""Coreason Manifest Package.
|
|
3
|
-
|
|
4
|
-
This package provides the core functionality for the Coreason Manifest system,
|
|
5
|
-
including loading, validation, policy enforcement, and integrity checking of
|
|
6
|
-
agent definitions.
|
|
7
|
-
|
|
8
|
-
The `coreason-manifest` package serves as the definitive source of truth for
|
|
9
|
-
Asset definitions in the CoReason-AI ecosystem.
|
|
10
|
-
|
|
11
|
-
Usage:
|
|
12
|
-
from coreason_manifest import ManifestEngine, ManifestConfig
|
|
13
|
-
|
|
14
|
-
config = ManifestConfig(policy_path="./policies/gx_compliant.rego")
|
|
15
|
-
engine = ManifestEngine(config)
|
|
16
|
-
agent_def = engine.load_and_validate("agent.yaml", "./src")
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
from .engine import ManifestConfig, ManifestEngine, ManifestEngineAsync
|
|
20
|
-
from .errors import (
|
|
21
|
-
IntegrityCompromisedError,
|
|
22
|
-
ManifestError,
|
|
23
|
-
ManifestSyntaxError,
|
|
24
|
-
PolicyViolationError,
|
|
25
|
-
)
|
|
26
|
-
from .integrity import IntegrityChecker
|
|
27
|
-
from .loader import ManifestLoader
|
|
28
|
-
from .models import (
|
|
29
|
-
AgentDefinition,
|
|
30
|
-
AgentDependencies,
|
|
31
|
-
AgentInterface,
|
|
32
|
-
AgentMetadata,
|
|
33
|
-
AgentTopology,
|
|
34
|
-
ModelConfig,
|
|
35
|
-
Step,
|
|
36
|
-
)
|
|
37
|
-
from .policy import PolicyEnforcer
|
|
38
|
-
from .recipes import (
|
|
39
|
-
AgentNode,
|
|
40
|
-
CouncilConfig,
|
|
41
|
-
Edge,
|
|
42
|
-
GraphTopology,
|
|
43
|
-
HumanNode,
|
|
44
|
-
LogicNode,
|
|
45
|
-
Node,
|
|
46
|
-
RecipeManifest,
|
|
47
|
-
VisualMetadata,
|
|
48
|
-
)
|
|
49
|
-
from .validator import SchemaValidator
|
|
50
|
-
|
|
51
|
-
__all__ = [
|
|
52
|
-
"AgentDefinition",
|
|
53
|
-
"AgentDependencies",
|
|
54
|
-
"AgentInterface",
|
|
55
|
-
"AgentMetadata",
|
|
56
|
-
"AgentNode",
|
|
57
|
-
"AgentTopology",
|
|
58
|
-
"CouncilConfig",
|
|
59
|
-
"Edge",
|
|
60
|
-
"GraphTopology",
|
|
61
|
-
"HumanNode",
|
|
62
|
-
"IntegrityChecker",
|
|
63
|
-
"IntegrityCompromisedError",
|
|
64
|
-
"LogicNode",
|
|
65
|
-
"ManifestConfig",
|
|
66
|
-
"ManifestEngine",
|
|
67
|
-
"ManifestEngineAsync",
|
|
68
|
-
"ManifestError",
|
|
69
|
-
"ManifestLoader",
|
|
70
|
-
"ManifestSyntaxError",
|
|
71
|
-
"ModelConfig",
|
|
72
|
-
"Node",
|
|
73
|
-
"PolicyEnforcer",
|
|
74
|
-
"PolicyViolationError",
|
|
75
|
-
"RecipeManifest",
|
|
76
|
-
"SchemaValidator",
|
|
77
|
-
"Step",
|
|
78
|
-
"VisualMetadata",
|
|
79
|
-
]
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
# Prosperity-3.0
|
|
2
|
-
"""Engine for the Coreason Manifest system.
|
|
3
|
-
|
|
4
|
-
This module provides the main entry point for verifying and loading Agent Manifests.
|
|
5
|
-
It coordinates schema validation, policy enforcement, and integrity checking.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import time
|
|
11
|
-
from dataclasses import dataclass, field
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from typing import Any, List, Optional, Union, cast
|
|
14
|
-
|
|
15
|
-
import anyio
|
|
16
|
-
import anyio.to_thread
|
|
17
|
-
|
|
18
|
-
from coreason_manifest.integrity import IntegrityChecker
|
|
19
|
-
from coreason_manifest.loader import ManifestLoader
|
|
20
|
-
from coreason_manifest.models import AgentDefinition
|
|
21
|
-
from coreason_manifest.policy import PolicyEnforcer
|
|
22
|
-
from coreason_manifest.utils.logger import logger
|
|
23
|
-
from coreason_manifest.validator import SchemaValidator
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@dataclass
|
|
27
|
-
class ManifestConfig:
|
|
28
|
-
"""Configuration for the ManifestEngine.
|
|
29
|
-
|
|
30
|
-
Attributes:
|
|
31
|
-
policy_path: Path to the Rego policy file.
|
|
32
|
-
opa_path: Path to the OPA executable. Defaults to "opa".
|
|
33
|
-
tbom_path: Optional path to the Trusted Bill of Materials.
|
|
34
|
-
extra_data_paths: Additional data paths to load into OPA.
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
policy_path: Union[str, Path]
|
|
38
|
-
opa_path: str = "opa"
|
|
39
|
-
tbom_path: Optional[Union[str, Path]] = None
|
|
40
|
-
extra_data_paths: List[Union[str, Path]] = field(default_factory=list)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class ManifestEngineAsync:
|
|
44
|
-
"""The async core for verifying and loading Agent Manifests.
|
|
45
|
-
|
|
46
|
-
This class coordinates the validation process, including:
|
|
47
|
-
1. Loading raw YAML.
|
|
48
|
-
2. Validating against JSON Schema.
|
|
49
|
-
3. Converting to AgentDefinition Pydantic model (Normalization).
|
|
50
|
-
4. Enforcing Policy (Rego).
|
|
51
|
-
5. Verifying Integrity (Hash check).
|
|
52
|
-
"""
|
|
53
|
-
|
|
54
|
-
def __init__(self, config: ManifestConfig) -> None:
|
|
55
|
-
"""Initialize the ManifestEngineAsync.
|
|
56
|
-
|
|
57
|
-
Args:
|
|
58
|
-
config: Configuration including policy path and OPA path.
|
|
59
|
-
"""
|
|
60
|
-
self.config = config
|
|
61
|
-
self.schema_validator = SchemaValidator()
|
|
62
|
-
|
|
63
|
-
# Collect data paths
|
|
64
|
-
data_paths = list(config.extra_data_paths)
|
|
65
|
-
if config.tbom_path:
|
|
66
|
-
data_paths.append(config.tbom_path)
|
|
67
|
-
|
|
68
|
-
self.policy_enforcer = PolicyEnforcer(
|
|
69
|
-
policy_path=config.policy_path,
|
|
70
|
-
opa_path=config.opa_path,
|
|
71
|
-
data_paths=data_paths,
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
async def __aenter__(self) -> ManifestEngineAsync:
|
|
75
|
-
"""Async context manager entry."""
|
|
76
|
-
return self
|
|
77
|
-
|
|
78
|
-
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
79
|
-
"""Async context manager exit."""
|
|
80
|
-
# Clean up resources if necessary.
|
|
81
|
-
pass
|
|
82
|
-
|
|
83
|
-
async def validate_manifest_dict(self, raw_data: dict[str, Any]) -> AgentDefinition:
|
|
84
|
-
"""Validates an Agent Manifest dictionary in memory.
|
|
85
|
-
|
|
86
|
-
Performs:
|
|
87
|
-
1. Normalization (stripping version prefixes)
|
|
88
|
-
2. Schema Validation
|
|
89
|
-
3. Model Conversion
|
|
90
|
-
4. Policy Enforcement
|
|
91
|
-
|
|
92
|
-
Does NOT perform Integrity Check (hashing).
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
raw_data: The raw dictionary of the manifest.
|
|
96
|
-
|
|
97
|
-
Returns:
|
|
98
|
-
AgentDefinition: The fully validated agent definition.
|
|
99
|
-
|
|
100
|
-
Raises:
|
|
101
|
-
ManifestSyntaxError: If structure or schema is invalid.
|
|
102
|
-
PolicyViolationError: If business rules are violated.
|
|
103
|
-
"""
|
|
104
|
-
# 1. Normalization (ensure version string is clean before schema/model validation)
|
|
105
|
-
# We access the static method on ManifestLoader.
|
|
106
|
-
ManifestLoader._normalize_data(raw_data)
|
|
107
|
-
|
|
108
|
-
# 2. Schema Validation
|
|
109
|
-
logger.debug("Running Schema Validation...")
|
|
110
|
-
self.schema_validator.validate(raw_data)
|
|
111
|
-
|
|
112
|
-
# 3. Model Conversion (Normalization) (CPU bound)
|
|
113
|
-
logger.debug("Converting to AgentDefinition...")
|
|
114
|
-
agent_def = await anyio.to_thread.run_sync(ManifestLoader.load_from_dict, raw_data)
|
|
115
|
-
logger.info(f"Validating Agent {agent_def.metadata.id} v{agent_def.metadata.version}")
|
|
116
|
-
|
|
117
|
-
# 4. Policy Enforcement (Subprocess / Blocking)
|
|
118
|
-
logger.debug("Enforcing Policies...")
|
|
119
|
-
# We assume policy is checked against the Normalized data (model dumped back to dict)
|
|
120
|
-
normalized_data = agent_def.model_dump(mode="json")
|
|
121
|
-
start_time = time.perf_counter()
|
|
122
|
-
try:
|
|
123
|
-
# PolicyEnforcer.evaluate is synchronous and runs subprocess.run, so we wrap it.
|
|
124
|
-
await anyio.to_thread.run_sync(self.policy_enforcer.evaluate, normalized_data)
|
|
125
|
-
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
126
|
-
logger.info(f"Policy Check: Pass - {duration_ms:.2f}ms")
|
|
127
|
-
except Exception:
|
|
128
|
-
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
129
|
-
logger.info(f"Policy Check: Fail - {duration_ms:.2f}ms")
|
|
130
|
-
raise
|
|
131
|
-
|
|
132
|
-
return cast(AgentDefinition, agent_def)
|
|
133
|
-
|
|
134
|
-
async def load_and_validate(self, manifest_path: Union[str, Path], source_dir: Union[str, Path]) -> AgentDefinition:
|
|
135
|
-
"""Loads, validates, and verifies an Agent Manifest asynchronously.
|
|
136
|
-
|
|
137
|
-
Args:
|
|
138
|
-
manifest_path: Path to the agent.yaml file.
|
|
139
|
-
source_dir: Path to the source code directory.
|
|
140
|
-
|
|
141
|
-
Returns:
|
|
142
|
-
AgentDefinition: The fully validated and verified agent definition.
|
|
143
|
-
|
|
144
|
-
Raises:
|
|
145
|
-
ManifestSyntaxError: If structure or schema is invalid.
|
|
146
|
-
PolicyViolationError: If business rules are violated.
|
|
147
|
-
IntegrityCompromisedError: If source code hash does not match.
|
|
148
|
-
FileNotFoundError: If files are missing.
|
|
149
|
-
"""
|
|
150
|
-
manifest_path = Path(manifest_path)
|
|
151
|
-
source_dir = Path(source_dir)
|
|
152
|
-
|
|
153
|
-
logger.info(f"Validating Agent Manifest: {manifest_path}")
|
|
154
|
-
|
|
155
|
-
# 1. Load Raw YAML (I/O)
|
|
156
|
-
raw_data = await ManifestLoader.load_raw_from_file_async(manifest_path)
|
|
157
|
-
|
|
158
|
-
# 2. Validate Manifest Dict (Schema, Model, Policy)
|
|
159
|
-
agent_def = await self.validate_manifest_dict(raw_data)
|
|
160
|
-
|
|
161
|
-
# 5. Integrity Check (Heavy I/O and CPU)
|
|
162
|
-
logger.debug("Verifying Integrity...")
|
|
163
|
-
# IntegrityChecker.verify is synchronous and does heavy IO, so we wrap it.
|
|
164
|
-
await anyio.to_thread.run_sync(IntegrityChecker.verify, agent_def, source_dir, manifest_path)
|
|
165
|
-
|
|
166
|
-
logger.info("Agent validation successful.")
|
|
167
|
-
return agent_def
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
class ManifestEngine:
|
|
171
|
-
"""The Sync Facade for ManifestEngineAsync.
|
|
172
|
-
|
|
173
|
-
Allows synchronous usage of the async core via anyio.run.
|
|
174
|
-
"""
|
|
175
|
-
|
|
176
|
-
def __init__(self, config: ManifestConfig) -> None:
|
|
177
|
-
"""Initialize the ManifestEngine facade.
|
|
178
|
-
|
|
179
|
-
Args:
|
|
180
|
-
config: Configuration including policy path and OPA path.
|
|
181
|
-
"""
|
|
182
|
-
self._async = ManifestEngineAsync(config)
|
|
183
|
-
|
|
184
|
-
def __getattr__(self, name: str) -> Any:
|
|
185
|
-
"""Delegate attribute access to the async engine instance.
|
|
186
|
-
|
|
187
|
-
This ensures backward compatibility for accessing attributes like
|
|
188
|
-
'config', 'schema_validator', and 'policy_enforcer'.
|
|
189
|
-
"""
|
|
190
|
-
return getattr(self._async, name)
|
|
191
|
-
|
|
192
|
-
def __enter__(self) -> ManifestEngine:
|
|
193
|
-
"""Context manager entry."""
|
|
194
|
-
anyio.run(self._async.__aenter__)
|
|
195
|
-
return self
|
|
196
|
-
|
|
197
|
-
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
198
|
-
"""Context manager exit."""
|
|
199
|
-
anyio.run(self._async.__aexit__, exc_type, exc_val, exc_tb)
|
|
200
|
-
|
|
201
|
-
def load_and_validate(self, manifest_path: Union[str, Path], source_dir: Union[str, Path]) -> AgentDefinition:
|
|
202
|
-
"""Loads, validates, and verifies an Agent Manifest synchronously.
|
|
203
|
-
|
|
204
|
-
Args:
|
|
205
|
-
manifest_path: Path to the agent.yaml file.
|
|
206
|
-
source_dir: Path to the source code directory.
|
|
207
|
-
|
|
208
|
-
Returns:
|
|
209
|
-
AgentDefinition: The fully validated and verified agent definition.
|
|
210
|
-
"""
|
|
211
|
-
return cast(AgentDefinition, anyio.run(self._async.load_and_validate, manifest_path, source_dir))
|
|
212
|
-
|
|
213
|
-
def validate_manifest_dict(self, raw_data: dict[str, Any]) -> AgentDefinition:
|
|
214
|
-
"""Validates an Agent Manifest dictionary synchronously.
|
|
215
|
-
|
|
216
|
-
Args:
|
|
217
|
-
raw_data: The raw dictionary of the manifest.
|
|
218
|
-
|
|
219
|
-
Returns:
|
|
220
|
-
AgentDefinition: The fully validated agent definition.
|
|
221
|
-
"""
|
|
222
|
-
return cast(AgentDefinition, anyio.run(self._async.validate_manifest_dict, raw_data))
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# Prosperity-3.0
|
|
2
|
-
"""Exceptions for the Coreason Manifest system.
|
|
3
|
-
|
|
4
|
-
This module defines the hierarchy of exceptions raised by the package.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class ManifestError(Exception):
|
|
11
|
-
"""Base exception for coreason_manifest errors."""
|
|
12
|
-
|
|
13
|
-
pass
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class ManifestSyntaxError(ManifestError):
|
|
17
|
-
"""Raised when the manifest YAML is invalid or missing required fields.
|
|
18
|
-
|
|
19
|
-
This includes YAML parsing errors and JSON Schema validation failures.
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
pass
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class PolicyViolationError(ManifestError):
|
|
26
|
-
"""Raised when the agent violates a compliance policy.
|
|
27
|
-
|
|
28
|
-
This error indicates that the manifest is structurally valid but fails
|
|
29
|
-
business rules or compliance checks (e.g., banned libraries).
|
|
30
|
-
|
|
31
|
-
Attributes:
|
|
32
|
-
violations: A list of specific policy violation messages.
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
def __init__(self, message: str, violations: list[str] | None = None) -> None:
|
|
36
|
-
"""Initialize PolicyViolationError.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
message: The error message.
|
|
40
|
-
violations: Optional list of detailed violation strings.
|
|
41
|
-
"""
|
|
42
|
-
super().__init__(message)
|
|
43
|
-
self.violations = violations or []
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class IntegrityCompromisedError(ManifestError):
|
|
47
|
-
"""Raised when the source code hash does not match the manifest.
|
|
48
|
-
|
|
49
|
-
This indicates that the source code may have been tampered with or changed
|
|
50
|
-
without updating the manifest's integrity hash.
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
pass
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
# Prosperity-3.0
|
|
2
|
-
"""Integrity checking functionality.
|
|
3
|
-
|
|
4
|
-
This module provides the `IntegrityChecker` class, which is responsible for
|
|
5
|
-
calculating deterministic hashes of source code directories and verifying
|
|
6
|
-
them against the expected hash in the agent manifest.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
import hashlib
|
|
12
|
-
import os
|
|
13
|
-
from pathlib import Path
|
|
14
|
-
from typing import List, Optional, Set, Union
|
|
15
|
-
|
|
16
|
-
from coreason_manifest.errors import IntegrityCompromisedError
|
|
17
|
-
from coreason_manifest.models import AgentDefinition
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class IntegrityChecker:
|
|
21
|
-
"""Component D: IntegrityChecker (The Notary).
|
|
22
|
-
|
|
23
|
-
Responsibility:
|
|
24
|
-
- Calculate the SHA256 hash of the source code directory.
|
|
25
|
-
- Compare it against the integrity_hash defined in the manifest.
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
IGNORED_DIRS = frozenset({".git", "__pycache__", ".venv", ".env", ".DS_Store"})
|
|
29
|
-
|
|
30
|
-
@staticmethod
|
|
31
|
-
def calculate_hash(source_dir: Union[Path, str], exclude_files: Optional[Set[Union[Path, str]]] = None) -> str:
|
|
32
|
-
"""Calculates a deterministic SHA256 hash of the source code directory.
|
|
33
|
-
|
|
34
|
-
It walks the directory using os.walk to efficiently prune ignored directories.
|
|
35
|
-
Sorts files by relative path, hashes each file, and then hashes the sequence.
|
|
36
|
-
|
|
37
|
-
Ignores hidden directories/files in IGNORED_DIRS.
|
|
38
|
-
Rejects symbolic links for security.
|
|
39
|
-
|
|
40
|
-
Args:
|
|
41
|
-
source_dir: The directory containing source code.
|
|
42
|
-
exclude_files: Optional set of file paths (absolute or relative to CWD)
|
|
43
|
-
to exclude from hashing.
|
|
44
|
-
|
|
45
|
-
Returns:
|
|
46
|
-
The hex digest of the SHA256 hash.
|
|
47
|
-
|
|
48
|
-
Raises:
|
|
49
|
-
FileNotFoundError: If source_dir does not exist.
|
|
50
|
-
IntegrityCompromisedError: If a symlink is found.
|
|
51
|
-
"""
|
|
52
|
-
path_obj = Path(source_dir)
|
|
53
|
-
if path_obj.is_symlink():
|
|
54
|
-
raise IntegrityCompromisedError(f"Symbolic links are forbidden: {path_obj}")
|
|
55
|
-
|
|
56
|
-
source_path = path_obj.resolve()
|
|
57
|
-
if not source_path.exists():
|
|
58
|
-
raise FileNotFoundError(f"Source directory not found: {source_path}")
|
|
59
|
-
|
|
60
|
-
# Normalize excluded files to absolute paths
|
|
61
|
-
excludes = set()
|
|
62
|
-
if exclude_files:
|
|
63
|
-
for ex_path in exclude_files:
|
|
64
|
-
excludes.add(Path(ex_path).resolve())
|
|
65
|
-
|
|
66
|
-
sha256 = hashlib.sha256()
|
|
67
|
-
file_paths: List[Path] = []
|
|
68
|
-
|
|
69
|
-
# Use os.walk for efficient traversal and pruning
|
|
70
|
-
for root, dirs, files in os.walk(source_path, topdown=True):
|
|
71
|
-
root_path = Path(root)
|
|
72
|
-
|
|
73
|
-
# Check for symlinks in directories before pruning
|
|
74
|
-
for d_name in dirs:
|
|
75
|
-
d_path = root_path / d_name
|
|
76
|
-
if d_path.is_symlink():
|
|
77
|
-
raise IntegrityCompromisedError(f"Symbolic links are forbidden: {d_path}") # pragma: no cover
|
|
78
|
-
|
|
79
|
-
# Prune directories efficiently using slice assignment
|
|
80
|
-
dirs[:] = [d for d in dirs if d not in IntegrityChecker.IGNORED_DIRS]
|
|
81
|
-
|
|
82
|
-
# Collect files
|
|
83
|
-
for f_name in files:
|
|
84
|
-
f_path = root_path / f_name
|
|
85
|
-
|
|
86
|
-
if f_path.is_symlink():
|
|
87
|
-
raise IntegrityCompromisedError(f"Symbolic links are forbidden: {f_path}")
|
|
88
|
-
|
|
89
|
-
if f_name in IntegrityChecker.IGNORED_DIRS:
|
|
90
|
-
continue
|
|
91
|
-
|
|
92
|
-
# Use resolved path for exclusion checking and inclusion
|
|
93
|
-
f_path_abs = f_path.resolve()
|
|
94
|
-
if f_path_abs in excludes:
|
|
95
|
-
continue
|
|
96
|
-
|
|
97
|
-
file_paths.append(f_path_abs)
|
|
98
|
-
|
|
99
|
-
# Sort to ensure deterministic order
|
|
100
|
-
# Use as_posix() to ensure ASCII sorting (case-sensitive) on all platforms (Windows vs Linux)
|
|
101
|
-
file_paths.sort(key=lambda p: p.relative_to(source_path).as_posix())
|
|
102
|
-
|
|
103
|
-
for path in file_paths:
|
|
104
|
-
# Update hash with relative path to ensure structure matters
|
|
105
|
-
# Use forward slashes for cross-platform consistency
|
|
106
|
-
rel_path = path.relative_to(source_path).as_posix().encode("utf-8")
|
|
107
|
-
sha256.update(rel_path)
|
|
108
|
-
|
|
109
|
-
# Update hash with file content
|
|
110
|
-
with open(path, "rb") as f:
|
|
111
|
-
while chunk := f.read(8192):
|
|
112
|
-
sha256.update(chunk)
|
|
113
|
-
|
|
114
|
-
return sha256.hexdigest()
|
|
115
|
-
|
|
116
|
-
@staticmethod
|
|
117
|
-
def verify(
|
|
118
|
-
agent_def: AgentDefinition,
|
|
119
|
-
source_dir: Union[Path, str],
|
|
120
|
-
manifest_path: Optional[Union[Path, str]] = None,
|
|
121
|
-
) -> None:
|
|
122
|
-
"""Verifies the integrity of the source code against the manifest.
|
|
123
|
-
|
|
124
|
-
Args:
|
|
125
|
-
agent_def: The AgentDefinition containing the expected hash.
|
|
126
|
-
source_dir: The directory containing source code.
|
|
127
|
-
manifest_path: Optional path to the manifest file to exclude from hashing.
|
|
128
|
-
|
|
129
|
-
Raises:
|
|
130
|
-
IntegrityCompromisedError: If the hash does not match or is missing.
|
|
131
|
-
FileNotFoundError: If source_dir does not exist.
|
|
132
|
-
"""
|
|
133
|
-
exclude_files = {manifest_path} if manifest_path else None
|
|
134
|
-
|
|
135
|
-
# agent_def.integrity_hash is now required by Pydantic model
|
|
136
|
-
calculated = IntegrityChecker.calculate_hash(source_dir, exclude_files=exclude_files)
|
|
137
|
-
|
|
138
|
-
if calculated != agent_def.integrity_hash:
|
|
139
|
-
raise IntegrityCompromisedError(
|
|
140
|
-
f"Integrity check failed. Expected {agent_def.integrity_hash}, got {calculated}"
|
|
141
|
-
)
|