coreason-manifest 0.13.0__tar.gz → 0.15.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.
Files changed (26) hide show
  1. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/PKG-INFO +1 -1
  2. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/pyproject.toml +2 -2
  3. coreason_manifest-0.15.0/src/coreason_manifest/__init__.py +97 -0
  4. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/common.py +2 -2
  5. coreason_manifest-0.15.0/src/coreason_manifest/definitions/identity.py +33 -0
  6. coreason_manifest-0.15.0/src/coreason_manifest/definitions/message.py +41 -0
  7. coreason_manifest-0.15.0/src/coreason_manifest/definitions/presentation.py +52 -0
  8. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/governance.py +2 -0
  9. coreason_manifest-0.15.0/src/coreason_manifest/spec/cap.py +115 -0
  10. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/v2/governance.py +5 -0
  11. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/v2/resolver.py +1 -3
  12. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/v2/spec/contracts.py +8 -6
  13. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/v2/spec/definitions.py +10 -61
  14. coreason_manifest-0.15.0/src/coreason_manifest/v2/validator.py +195 -0
  15. coreason_manifest-0.13.0/src/coreason_manifest/__init__.py +0 -51
  16. coreason_manifest-0.13.0/src/coreason_manifest/v2/validator.py +0 -48
  17. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/LICENSE +0 -0
  18. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/NOTICE +0 -0
  19. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/README.md +0 -0
  20. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/schemas/__init__.py +0 -0
  21. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/schemas/coreason-v2.schema.json +0 -0
  22. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/utils/__init__.py +0 -0
  23. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/utils/logger.py +0 -0
  24. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/v2/__init__.py +0 -0
  25. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/v2/io.py +0 -0
  26. {coreason_manifest-0.13.0 → coreason_manifest-0.15.0}/src/coreason_manifest/v2/spec/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coreason_manifest
3
- Version: 0.13.0
3
+ Version: 0.15.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
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "coreason_manifest"
3
- version = "0.13.0"
3
+ version = "0.15.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"
@@ -32,7 +32,7 @@ build-backend = "poetry.core.masonry.api"
32
32
 
33
33
  [project]
34
34
  name = "coreason_manifest"
35
- version = "0.13.0"
35
+ version = "0.15.0"
36
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."
37
37
  readme = "README.md"
38
38
  requires-python = ">=3.12"
@@ -0,0 +1,97 @@
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-manifest
10
+
11
+ from .common import ToolRiskLevel
12
+ from .definitions.identity import Identity
13
+ from .definitions.message import ChatMessage, Role
14
+ from .definitions.presentation import (
15
+ AnyPresentationEvent,
16
+ ArtifactEvent,
17
+ CitationEvent,
18
+ PresentationEvent,
19
+ PresentationEventType,
20
+ )
21
+ from .governance import ComplianceReport, ComplianceViolation, GovernanceConfig
22
+ from .spec.cap import (
23
+ ErrorSeverity,
24
+ HealthCheckResponse,
25
+ HealthCheckStatus,
26
+ ServiceRequest,
27
+ ServiceResponse,
28
+ StreamError,
29
+ StreamOpCode,
30
+ StreamPacket,
31
+ )
32
+ from .v2.governance import check_compliance_v2
33
+ from .v2.io import dump_to_yaml, load_from_yaml
34
+ from .v2.spec.contracts import InterfaceDefinition, PolicyDefinition, StateDefinition
35
+ from .v2.spec.definitions import (
36
+ AgentDefinition,
37
+ AgentStep,
38
+ CouncilStep,
39
+ LogicStep,
40
+ ManifestMetadata,
41
+ ManifestV2,
42
+ Step,
43
+ SwitchStep,
44
+ ToolDefinition,
45
+ Workflow,
46
+ )
47
+ from .v2.validator import validate_integrity, validate_loose
48
+
49
+ __version__ = "0.15.0"
50
+
51
+ Manifest = ManifestV2
52
+ Recipe = ManifestV2
53
+ load = load_from_yaml
54
+ dump = dump_to_yaml
55
+
56
+ __all__ = [
57
+ "Manifest",
58
+ "Recipe",
59
+ "load",
60
+ "dump",
61
+ "__version__",
62
+ "ManifestMetadata",
63
+ "AgentStep",
64
+ "Workflow",
65
+ "AgentDefinition",
66
+ "ToolDefinition",
67
+ "Step",
68
+ "LogicStep",
69
+ "SwitchStep",
70
+ "CouncilStep",
71
+ "InterfaceDefinition",
72
+ "StateDefinition",
73
+ "PolicyDefinition",
74
+ "ToolRiskLevel",
75
+ "GovernanceConfig",
76
+ "ComplianceReport",
77
+ "ComplianceViolation",
78
+ "Identity",
79
+ "Role",
80
+ "ChatMessage",
81
+ "PresentationEventType",
82
+ "PresentationEvent",
83
+ "AnyPresentationEvent",
84
+ "CitationEvent",
85
+ "ArtifactEvent",
86
+ "HealthCheckResponse",
87
+ "HealthCheckStatus",
88
+ "ServiceRequest",
89
+ "ServiceResponse",
90
+ "StreamPacket",
91
+ "StreamError",
92
+ "StreamOpCode",
93
+ "ErrorSeverity",
94
+ "validate_integrity",
95
+ "validate_loose",
96
+ "check_compliance_v2",
97
+ ]
@@ -32,8 +32,8 @@ class CoReasonBaseModel(BaseModel):
32
32
  Uses mode='json' to ensure types like UUID and datetime are serialized to strings.
33
33
  Defaults to by_alias=True and exclude_none=True.
34
34
  """
35
- # Set defaults but allow overrides
36
- kwargs.setdefault("mode", "json")
35
+ # Strict enforcement of json mode for zero-friction serialization
36
+ kwargs["mode"] = "json"
37
37
  kwargs.setdefault("by_alias", True)
38
38
  kwargs.setdefault("exclude_none", True)
39
39
  return self.model_dump(**kwargs)
@@ -0,0 +1,33 @@
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-manifest
10
+
11
+ from typing import Optional
12
+
13
+ from pydantic import ConfigDict, Field
14
+
15
+ from ..common import CoReasonBaseModel
16
+
17
+
18
+ class Identity(CoReasonBaseModel):
19
+ """A pure, frozen data structure representing an actor (user, agent, system)."""
20
+
21
+ model_config = ConfigDict(frozen=True)
22
+
23
+ id: str = Field(..., description="Unique identifier for the actor (e.g., UUID, slug).")
24
+ name: str = Field(..., description="Human-readable display name.")
25
+ role: Optional[str] = Field(None, description="Contextual role (e.g., 'user', 'assistant', 'system').")
26
+
27
+ def __str__(self) -> str:
28
+ return f"{self.name} ({self.id})"
29
+
30
+ @classmethod
31
+ def anonymous(cls) -> "Identity":
32
+ """Factory method for a standardized anonymous identity."""
33
+ return cls(id="anonymous", name="Anonymous User", role="user")
@@ -0,0 +1,41 @@
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-manifest
10
+
11
+ from datetime import datetime, timezone
12
+ from enum import Enum
13
+ from typing import Optional
14
+
15
+ from pydantic import ConfigDict, Field
16
+
17
+ from ..common import CoReasonBaseModel
18
+
19
+
20
+ class Role(str, Enum):
21
+ """The role of the message sender."""
22
+
23
+ SYSTEM = "system"
24
+ USER = "user"
25
+ ASSISTANT = "assistant"
26
+ TOOL = "tool"
27
+
28
+
29
+ class ChatMessage(CoReasonBaseModel):
30
+ """A single message in a conversation."""
31
+
32
+ model_config = ConfigDict(frozen=True)
33
+
34
+ role: Role = Field(..., description="The role of the message sender.")
35
+ content: str = Field(..., description="The content of the message.")
36
+ name: Optional[str] = Field(None, description="The name of the author of this message.")
37
+ tool_call_id: Optional[str] = Field(None, description="The tool call ID this message is responding to.")
38
+ timestamp: datetime = Field(
39
+ default_factory=lambda: datetime.now(timezone.utc),
40
+ description="The timestamp of the message.",
41
+ )
@@ -0,0 +1,52 @@
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-manifest
10
+
11
+ from enum import Enum
12
+ from typing import List, Literal, Optional, Union
13
+
14
+ from pydantic import ConfigDict, Field
15
+
16
+ from ..common import CoReasonBaseModel
17
+
18
+
19
+ class PresentationEventType(str, Enum):
20
+ """Types of presentation events."""
21
+
22
+ CITATION = "citation"
23
+ ARTIFACT = "artifact"
24
+
25
+
26
+ class PresentationEvent(CoReasonBaseModel):
27
+ """Base class for presentation events."""
28
+
29
+ model_config = ConfigDict(frozen=True)
30
+
31
+ type: PresentationEventType = Field(..., description="The type of presentation event.")
32
+
33
+
34
+ class CitationEvent(PresentationEvent):
35
+ """An event representing a citation."""
36
+
37
+ type: Literal[PresentationEventType.CITATION] = PresentationEventType.CITATION
38
+ uri: str = Field(..., description="The source URI.")
39
+ text: str = Field(..., description="The quoted text.")
40
+ indices: Optional[List[int]] = Field(None, description="Start and end character indices.")
41
+
42
+
43
+ class ArtifactEvent(PresentationEvent):
44
+ """An event representing a generated artifact."""
45
+
46
+ type: Literal[PresentationEventType.ARTIFACT] = PresentationEventType.ARTIFACT
47
+ artifact_id: str = Field(..., description="Unique ID of the artifact.")
48
+ mime_type: str = Field(..., description="MIME type of the artifact.")
49
+ url: Optional[str] = Field(None, description="Download URL if applicable.")
50
+
51
+
52
+ AnyPresentationEvent = Union[CitationEvent, ArtifactEvent]
@@ -60,6 +60,7 @@ class ComplianceViolation(CoReasonBaseModel):
60
60
  rule: Name of the rule broken.
61
61
  message: Human readable details.
62
62
  component_id: Name of the tool or component causing the issue.
63
+ severity: Severity level (e.g., 'error', 'warning').
63
64
  """
64
65
 
65
66
  model_config = ConfigDict(extra="forbid", frozen=True)
@@ -67,6 +68,7 @@ class ComplianceViolation(CoReasonBaseModel):
67
68
  rule: str = Field(..., description="Name of the rule broken, e.g., 'domain_restriction'.")
68
69
  message: str = Field(..., description="Human readable details.")
69
70
  component_id: Optional[str] = Field(None, description="Name of the tool or component causing the issue.")
71
+ severity: Optional[str] = Field(None, description="Severity level (e.g., 'error', 'warning').")
70
72
 
71
73
 
72
74
  class ComplianceReport(CoReasonBaseModel):
@@ -0,0 +1,115 @@
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-manifest
10
+
11
+ from datetime import datetime
12
+ from enum import Enum
13
+ from typing import Any, Dict, Optional, Union
14
+ from uuid import UUID
15
+
16
+ from pydantic import ConfigDict, Field, model_validator
17
+
18
+ from coreason_manifest.common import CoReasonBaseModel
19
+
20
+
21
+ class HealthCheckStatus(str, Enum):
22
+ """Status of the health check."""
23
+
24
+ OK = "ok"
25
+ DEGRADED = "degraded"
26
+ MAINTENANCE = "maintenance"
27
+
28
+
29
+ class HealthCheckResponse(CoReasonBaseModel):
30
+ """Response for a health check request."""
31
+
32
+ model_config = ConfigDict(frozen=True)
33
+
34
+ status: HealthCheckStatus
35
+ agent_id: UUID
36
+ version: str
37
+ uptime_seconds: float
38
+
39
+
40
+ class ErrorSeverity(str, Enum):
41
+ """Severity of a stream error."""
42
+
43
+ TRANSIENT = "transient"
44
+ FATAL = "fatal"
45
+
46
+
47
+ class StreamOpCode(str, Enum):
48
+ """Operation code for a stream packet."""
49
+
50
+ DELTA = "delta"
51
+ EVENT = "event"
52
+ ERROR = "error"
53
+ CLOSE = "close"
54
+
55
+
56
+ class StreamError(CoReasonBaseModel):
57
+ """Strict error model for stream exceptions."""
58
+
59
+ model_config = ConfigDict(frozen=True)
60
+
61
+ code: str
62
+ message: str
63
+ severity: ErrorSeverity
64
+ details: Optional[Dict[str, Any]] = None
65
+
66
+
67
+ class StreamPacket(CoReasonBaseModel):
68
+ """A packet of data streaming from an agent."""
69
+
70
+ model_config = ConfigDict(frozen=True)
71
+
72
+ op: StreamOpCode
73
+ p: Union[StreamError, str, Dict[str, Any], None] = Field(union_mode="left_to_right")
74
+
75
+ @model_validator(mode="after")
76
+ def validate_structure(self) -> "StreamPacket":
77
+ if self.op == StreamOpCode.ERROR:
78
+ if not isinstance(self.p, StreamError):
79
+ raise ValueError("Payload 'p' must be a valid StreamError when op is ERROR.")
80
+
81
+ if self.op == StreamOpCode.DELTA:
82
+ if not isinstance(self.p, str):
83
+ raise ValueError("Payload 'p' must be a string when op is DELTA.")
84
+
85
+ return self
86
+
87
+
88
+ class ServiceResponse(CoReasonBaseModel):
89
+ """Synchronous response from an agent service."""
90
+
91
+ model_config = ConfigDict(frozen=True)
92
+
93
+ request_id: UUID
94
+ created_at: datetime
95
+ output: Dict[str, Any]
96
+ metrics: Optional[Dict[str, Any]] = None
97
+
98
+
99
+ class ServiceRequest(CoReasonBaseModel):
100
+ """Request to an agent service.
101
+
102
+ Attributes:
103
+ request_id: Unique trace ID for the transaction.
104
+ context: Metadata about the request (User Identity, Auth, Session).
105
+ Separated from logic to enable consistent security policies.
106
+ payload: The actual arguments for the Agent's execution.
107
+ """
108
+
109
+ model_config = ConfigDict(frozen=True)
110
+
111
+ request_id: UUID
112
+ # TODO: In v0.16.0, strictly type 'context' with a SessionContext model
113
+ # once the Identity primitive is fully integrated.
114
+ context: Dict[str, Any]
115
+ payload: Dict[str, Any]
@@ -41,6 +41,11 @@ def _risk_score(level: ToolRiskLevel) -> int:
41
41
  def check_compliance_v2(manifest: ManifestV2, config: GovernanceConfig) -> ComplianceReport:
42
42
  """Enforce policy on V2 Manifest before compilation.
43
43
 
44
+ Validates:
45
+ - Tool risk levels against allowed maximums.
46
+ - Tool URIs against allowed domains.
47
+ - Presence of custom logic (LogicStep, complex SwitchStep) if restricted.
48
+
44
49
  Args:
45
50
  manifest: The V2 manifest to check.
46
51
  config: The governance policy configuration.
@@ -57,9 +57,7 @@ class ReferenceResolver:
57
57
  try:
58
58
  target_path.relative_to(self.root_dir)
59
59
  except ValueError:
60
- raise ValueError(
61
- f"Security Error: Reference '{ref_path}' escapes the root directory '{self.root_dir}'."
62
- ) from None
60
+ raise ValueError(f"Security Error: Reference '{ref_path}' escapes the root directory.") from None
63
61
 
64
62
  if not target_path.exists():
65
63
  raise FileNotFoundError(f"Referenced file not found: {target_path}")
@@ -1,18 +1,20 @@
1
1
  from typing import Any, Dict, Optional
2
2
 
3
- from pydantic import BaseModel, ConfigDict, Field
3
+ from pydantic import ConfigDict, Field
4
4
 
5
+ from coreason_manifest.common import CoReasonBaseModel
5
6
 
6
- class InterfaceDefinition(BaseModel):
7
+
8
+ class InterfaceDefinition(CoReasonBaseModel):
7
9
  """Defines the input/output contract."""
8
10
 
9
- model_config = ConfigDict(extra="forbid")
11
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
10
12
 
11
13
  inputs: Dict[str, Any] = Field(default_factory=dict, description="JSON Schema definitions for arguments.")
12
14
  outputs: Dict[str, Any] = Field(default_factory=dict, description="JSON Schema definitions for return values.")
13
15
 
14
16
 
15
- class StateDefinition(BaseModel):
17
+ class StateDefinition(CoReasonBaseModel):
16
18
  """Defines the conversation memory/context structure."""
17
19
 
18
20
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -23,10 +25,10 @@ class StateDefinition(BaseModel):
23
25
  backend: Optional[str] = Field(None, description="Backend storage type (e.g., 'redis', 'memory').")
24
26
 
25
27
 
26
- class PolicyDefinition(BaseModel):
28
+ class PolicyDefinition(CoReasonBaseModel):
27
29
  """Defines execution policy and governance rules."""
28
30
 
29
- model_config = ConfigDict(extra="forbid")
31
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
30
32
 
31
33
  max_steps: Optional[int] = Field(None, description="Execution limit on number of steps.")
32
34
  max_retries: int = Field(3, description="Maximum number of retries.")
@@ -1,12 +1,12 @@
1
1
  from typing import Annotated, Any, Dict, List, Literal, Optional, Union
2
2
 
3
- from pydantic import BaseModel, ConfigDict, Field, model_validator
3
+ from pydantic import ConfigDict, Field
4
4
 
5
- from coreason_manifest.common import StrictUri, ToolRiskLevel
5
+ from coreason_manifest.common import CoReasonBaseModel, StrictUri, ToolRiskLevel
6
6
  from coreason_manifest.v2.spec.contracts import InterfaceDefinition, PolicyDefinition, StateDefinition
7
7
 
8
8
 
9
- class DesignMetadata(BaseModel):
9
+ class DesignMetadata(CoReasonBaseModel):
10
10
  """UI-specific metadata for the visual builder."""
11
11
 
12
12
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -20,7 +20,7 @@ class DesignMetadata(BaseModel):
20
20
  collapsed: bool = Field(False, description="Whether the node is collapsed in UI.")
21
21
 
22
22
 
23
- class ToolDefinition(BaseModel):
23
+ class ToolDefinition(CoReasonBaseModel):
24
24
  """Definition of an external tool."""
25
25
 
26
26
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -33,7 +33,7 @@ class ToolDefinition(BaseModel):
33
33
  description: Optional[str] = Field(None, description="Description of the tool.")
34
34
 
35
35
 
36
- class AgentDefinition(BaseModel):
36
+ class AgentDefinition(CoReasonBaseModel):
37
37
  """Definition of an Agent."""
38
38
 
39
39
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -49,13 +49,13 @@ class AgentDefinition(BaseModel):
49
49
  knowledge: List[str] = Field(default_factory=list, description="List of file paths or knowledge base IDs.")
50
50
 
51
51
 
52
- class GenericDefinition(BaseModel):
52
+ class GenericDefinition(CoReasonBaseModel):
53
53
  """Fallback for unknown definitions."""
54
54
 
55
55
  model_config = ConfigDict(extra="allow")
56
56
 
57
57
 
58
- class BaseStep(BaseModel):
58
+ class BaseStep(CoReasonBaseModel):
59
59
  """Base attributes for all steps."""
60
60
 
61
61
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -106,7 +106,7 @@ Step = Annotated[
106
106
  ]
107
107
 
108
108
 
109
- class Workflow(BaseModel):
109
+ class Workflow(CoReasonBaseModel):
110
110
  """Defines the execution topology."""
111
111
 
112
112
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -115,7 +115,7 @@ class Workflow(BaseModel):
115
115
  steps: Dict[str, Step] = Field(..., description="Dictionary of all steps indexed by ID.")
116
116
 
117
117
 
118
- class ManifestMetadata(BaseModel):
118
+ class ManifestMetadata(CoReasonBaseModel):
119
119
  """Metadata for the manifest."""
120
120
 
121
121
  model_config = ConfigDict(extra="allow", populate_by_name=True)
@@ -124,7 +124,7 @@ class ManifestMetadata(BaseModel):
124
124
  design_metadata: Optional[DesignMetadata] = Field(None, alias="x-design", description="UI metadata.")
125
125
 
126
126
 
127
- class ManifestV2(BaseModel):
127
+ class ManifestV2(CoReasonBaseModel):
128
128
  """Root object for Coreason Manifest V2."""
129
129
 
130
130
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -143,54 +143,3 @@ class ManifestV2(BaseModel):
143
143
  ],
144
144
  ] = Field(default_factory=dict, description="Reusable definitions.")
145
145
  workflow: Workflow = Field(..., description="The main workflow topology.")
146
-
147
- @model_validator(mode="after")
148
- def validate_integrity(self) -> "ManifestV2":
149
- """Validate referential integrity of the manifest."""
150
- steps = self.workflow.steps
151
-
152
- # 1. Validate Start Step
153
- if self.workflow.start not in steps:
154
- raise ValueError(f"Start step '{self.workflow.start}' not found in steps.")
155
-
156
- for step in steps.values():
157
- # 2. Validate 'next' pointers (AgentStep, LogicStep, CouncilStep)
158
- if hasattr(step, "next") and step.next:
159
- if step.next not in steps:
160
- raise ValueError(f"Step '{step.id}' references missing next step '{step.next}'.")
161
-
162
- # 3. Validate SwitchStep targets
163
- if isinstance(step, SwitchStep):
164
- for target in step.cases.values():
165
- if target not in steps:
166
- raise ValueError(f"SwitchStep '{step.id}' references missing step '{target}' in cases.")
167
- if step.default and step.default not in steps:
168
- raise ValueError(f"SwitchStep '{step.id}' references missing step '{step.default}' in default.")
169
-
170
- # 4. Validate Definition References
171
- if isinstance(step, AgentStep):
172
- if step.agent not in self.definitions:
173
- raise ValueError(f"AgentStep '{step.id}' references missing agent '{step.agent}'.")
174
-
175
- # Check type
176
- agent_def = self.definitions[step.agent]
177
- if not isinstance(agent_def, AgentDefinition):
178
- raise ValueError(
179
- f"AgentStep '{step.id}' references '{step.agent}' which is not an AgentDefinition "
180
- f"(got {type(agent_def).__name__})."
181
- )
182
-
183
- if isinstance(step, CouncilStep):
184
- for voter in step.voters:
185
- if voter not in self.definitions:
186
- raise ValueError(f"CouncilStep '{step.id}' references missing voter '{voter}'.")
187
-
188
- # Check type
189
- agent_def = self.definitions[voter]
190
- if not isinstance(agent_def, AgentDefinition):
191
- raise ValueError(
192
- f"CouncilStep '{step.id}' references voter '{voter}' which is not an AgentDefinition "
193
- f"(got {type(agent_def).__name__})."
194
- )
195
-
196
- return self
@@ -0,0 +1,195 @@
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-manifest
10
+
11
+ """Validation logic for V2 Manifests."""
12
+
13
+ from typing import List
14
+
15
+ from coreason_manifest.v2.spec.definitions import (
16
+ AgentDefinition,
17
+ AgentStep,
18
+ CouncilStep,
19
+ ManifestV2,
20
+ SwitchStep,
21
+ ToolDefinition,
22
+ )
23
+
24
+
25
+ def validate_integrity(manifest: ManifestV2) -> ManifestV2:
26
+ """Validate referential integrity of the manifest.
27
+
28
+ Checks:
29
+ 1. workflow.start exists in steps.
30
+ 2. step.next pointers exist in steps.
31
+ 3. SwitchStep.cases targets exist.
32
+ 4. agent references in AgentStep exist in definitions.
33
+ 5. CouncilStep voters exist in definitions.
34
+ 6. AgentDefinition tools exist in definitions.
35
+
36
+ Args:
37
+ manifest: The V2 manifest to validate.
38
+
39
+ Returns:
40
+ The valid manifest (for chaining).
41
+
42
+ Raises:
43
+ ValueError: If referential integrity is violated.
44
+ """
45
+ steps = manifest.workflow.steps
46
+
47
+ # 1. Validate Start Step
48
+ if manifest.workflow.start not in steps:
49
+ raise ValueError(f"Start step '{manifest.workflow.start}' not found in steps.")
50
+
51
+ for step in steps.values():
52
+ # 2. Validate 'next' pointers (AgentStep, LogicStep, CouncilStep)
53
+ if hasattr(step, "next") and step.next:
54
+ if step.next not in steps:
55
+ raise ValueError(f"Step '{step.id}' references missing next step '{step.next}'.")
56
+
57
+ # 3. Validate SwitchStep targets
58
+ if isinstance(step, SwitchStep):
59
+ for target in step.cases.values():
60
+ if target not in steps:
61
+ raise ValueError(f"SwitchStep '{step.id}' references missing step '{target}' in cases.")
62
+ if step.default and step.default not in steps:
63
+ raise ValueError(f"SwitchStep '{step.id}' references missing step '{step.default}' in default.")
64
+
65
+ # 4. Validate Definition References
66
+ if isinstance(step, AgentStep):
67
+ if step.agent not in manifest.definitions:
68
+ raise ValueError(f"AgentStep '{step.id}' references missing agent '{step.agent}'.")
69
+
70
+ # Check type
71
+ agent_def = manifest.definitions[step.agent]
72
+ if not isinstance(agent_def, AgentDefinition):
73
+ raise ValueError(
74
+ f"AgentStep '{step.id}' references '{step.agent}' which is not an AgentDefinition "
75
+ f"(got {type(agent_def).__name__})."
76
+ )
77
+
78
+ if isinstance(step, CouncilStep):
79
+ for voter in step.voters:
80
+ if voter not in manifest.definitions:
81
+ raise ValueError(f"CouncilStep '{step.id}' references missing voter '{voter}'.")
82
+
83
+ # Check type
84
+ agent_def = manifest.definitions[voter]
85
+ if not isinstance(agent_def, AgentDefinition):
86
+ raise ValueError(
87
+ f"CouncilStep '{step.id}' references voter '{voter}' which is not an AgentDefinition "
88
+ f"(got {type(agent_def).__name__})."
89
+ )
90
+
91
+ # 5. Validate Agent Tools
92
+ for _, definition in manifest.definitions.items():
93
+ if isinstance(definition, AgentDefinition):
94
+ for tool_id in definition.tools:
95
+ if tool_id not in manifest.definitions:
96
+ raise ValueError(f"Agent '{definition.id}' references missing tool '{tool_id}'.")
97
+
98
+ tool_def = manifest.definitions[tool_id]
99
+ if not isinstance(tool_def, ToolDefinition):
100
+ raise ValueError(
101
+ f"Agent '{definition.id}' references '{tool_id}' which is not a ToolDefinition "
102
+ f"(got {type(tool_def).__name__})."
103
+ )
104
+
105
+ return manifest
106
+
107
+
108
+ def validate_loose(manifest: ManifestV2) -> List[str]:
109
+ """Validate "Draft" manifests for structural sanity only.
110
+
111
+ Checks:
112
+ - Unique Step IDs.
113
+ - SwitchStep case syntax (basic).
114
+ - Referential integrity (Start, Next, Switch, Agents, Tools) - warnings only.
115
+
116
+ Args:
117
+ manifest: The V2 manifest to validate.
118
+
119
+ Returns:
120
+ List of warning messages (empty if clean).
121
+ """
122
+ warnings: List[str] = []
123
+
124
+ # 1. Check unique Step IDs
125
+ for step_id, step in manifest.workflow.steps.items():
126
+ if step.id != step_id:
127
+ warnings.append(f"Step key '{step_id}' does not match step.id '{step.id}'.")
128
+
129
+ # 2. Check SwitchStep cases syntax
130
+ for step_id, step in manifest.workflow.steps.items():
131
+ if isinstance(step, SwitchStep):
132
+ for condition in step.cases.keys():
133
+ if not isinstance(condition, str) or not condition.strip():
134
+ warnings.append(f"SwitchStep '{step_id}' has invalid condition: {condition}")
135
+
136
+ # 3. Check Referential Integrity (Warnings)
137
+ steps = manifest.workflow.steps
138
+
139
+ # Start Step
140
+ if manifest.workflow.start not in steps:
141
+ warnings.append(f"Start step '{manifest.workflow.start}' not found in steps.")
142
+
143
+ for step in steps.values():
144
+ # 'next' pointers
145
+ if hasattr(step, "next") and step.next:
146
+ if step.next not in steps:
147
+ warnings.append(f"Step '{step.id}' references missing next step '{step.next}'.")
148
+
149
+ # SwitchStep targets
150
+ if isinstance(step, SwitchStep):
151
+ for target in step.cases.values():
152
+ if target not in steps:
153
+ warnings.append(f"SwitchStep '{step.id}' references missing step '{target}' in cases.")
154
+ if step.default and step.default not in steps:
155
+ warnings.append(f"SwitchStep '{step.id}' references missing step '{step.default}' in default.")
156
+
157
+ # Definition References
158
+ if isinstance(step, AgentStep):
159
+ if step.agent not in manifest.definitions:
160
+ warnings.append(f"AgentStep '{step.id}' references missing agent '{step.agent}'.")
161
+ else:
162
+ agent_def = manifest.definitions[step.agent]
163
+ if not isinstance(agent_def, AgentDefinition):
164
+ warnings.append(
165
+ f"AgentStep '{step.id}' references '{step.agent}' which is not an AgentDefinition "
166
+ f"(got {type(agent_def).__name__})."
167
+ )
168
+
169
+ if isinstance(step, CouncilStep):
170
+ for voter in step.voters:
171
+ if voter not in manifest.definitions:
172
+ warnings.append(f"CouncilStep '{step.id}' references missing voter '{voter}'.")
173
+ else:
174
+ agent_def = manifest.definitions[voter]
175
+ if not isinstance(agent_def, AgentDefinition):
176
+ warnings.append(
177
+ f"CouncilStep '{step.id}' references voter '{voter}' which is not an AgentDefinition "
178
+ f"(got {type(agent_def).__name__})."
179
+ )
180
+
181
+ # Agent Tools
182
+ for _, definition in manifest.definitions.items():
183
+ if isinstance(definition, AgentDefinition):
184
+ for tool_id in definition.tools:
185
+ if tool_id not in manifest.definitions:
186
+ warnings.append(f"Agent '{definition.id}' references missing tool '{tool_id}'.")
187
+ else:
188
+ tool_def = manifest.definitions[tool_id]
189
+ if not isinstance(tool_def, ToolDefinition):
190
+ warnings.append(
191
+ f"Agent '{definition.id}' references '{tool_id}' which is not a ToolDefinition "
192
+ f"(got {type(tool_def).__name__})."
193
+ )
194
+
195
+ return warnings
@@ -1,51 +0,0 @@
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-manifest
10
-
11
- from .v2.io import dump_to_yaml, load_from_yaml
12
- from .v2.spec.contracts import InterfaceDefinition, PolicyDefinition, StateDefinition
13
- from .v2.spec.definitions import (
14
- AgentDefinition,
15
- AgentStep,
16
- CouncilStep,
17
- LogicStep,
18
- ManifestMetadata,
19
- ManifestV2,
20
- Step,
21
- SwitchStep,
22
- ToolDefinition,
23
- Workflow,
24
- )
25
-
26
- __version__ = "0.13.0"
27
-
28
- Manifest = ManifestV2
29
- Recipe = ManifestV2
30
- load = load_from_yaml
31
- dump = dump_to_yaml
32
-
33
- __all__ = [
34
- "Manifest",
35
- "Recipe",
36
- "load",
37
- "dump",
38
- "__version__",
39
- "ManifestMetadata",
40
- "AgentStep",
41
- "Workflow",
42
- "AgentDefinition",
43
- "ToolDefinition",
44
- "Step",
45
- "LogicStep",
46
- "SwitchStep",
47
- "CouncilStep",
48
- "InterfaceDefinition",
49
- "StateDefinition",
50
- "PolicyDefinition",
51
- ]
@@ -1,48 +0,0 @@
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-manifest
10
-
11
- """Validation logic for V2 Manifests."""
12
-
13
- from typing import List
14
-
15
- from coreason_manifest.v2.spec.definitions import (
16
- ManifestV2,
17
- SwitchStep,
18
- )
19
-
20
-
21
- def validate_loose(manifest: ManifestV2) -> List[str]:
22
- """Validate "Draft" manifests for structural sanity only.
23
-
24
- Checks:
25
- - Unique Step IDs.
26
- - SwitchStep case syntax (basic).
27
-
28
- Args:
29
- manifest: The V2 manifest to validate.
30
-
31
- Returns:
32
- List of warning messages (empty if clean).
33
- """
34
- warnings: List[str] = []
35
-
36
- # 1. Check unique Step IDs
37
- for step_id, step in manifest.workflow.steps.items():
38
- if step.id != step_id:
39
- warnings.append(f"Step key '{step_id}' does not match step.id '{step.id}'.")
40
-
41
- # 2. Check SwitchStep cases
42
- for step_id, step in manifest.workflow.steps.items():
43
- if isinstance(step, SwitchStep):
44
- for condition in step.cases.keys():
45
- if not isinstance(condition, str) or not condition.strip():
46
- warnings.append(f"SwitchStep '{step_id}' has invalid condition: {condition}")
47
-
48
- return warnings