coreason-manifest 0.13.0__py3-none-any.whl → 0.16.0__py3-none-any.whl

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.
@@ -8,6 +8,30 @@
8
8
  #
9
9
  # Source Code: https://github.com/CoReason-AI/coreason-manifest
10
10
 
11
+ from .common import ToolRiskLevel
12
+ from .definitions.capabilities import AgentCapabilities, DeliveryMode
13
+ from .definitions.identity import Identity
14
+ from .definitions.message import ChatMessage, Role
15
+ from .definitions.presentation import (
16
+ AnyPresentationEvent,
17
+ ArtifactEvent,
18
+ CitationEvent,
19
+ PresentationEvent,
20
+ PresentationEventType,
21
+ )
22
+ from .definitions.service import AgentRequest, ServiceContract
23
+ from .governance import ComplianceReport, ComplianceViolation, GovernanceConfig
24
+ from .spec.cap import (
25
+ ErrorSeverity,
26
+ HealthCheckResponse,
27
+ HealthCheckStatus,
28
+ ServiceRequest,
29
+ ServiceResponse,
30
+ StreamError,
31
+ StreamOpCode,
32
+ StreamPacket,
33
+ )
34
+ from .v2.governance import check_compliance_v2
11
35
  from .v2.io import dump_to_yaml, load_from_yaml
12
36
  from .v2.spec.contracts import InterfaceDefinition, PolicyDefinition, StateDefinition
13
37
  from .v2.spec.definitions import (
@@ -22,8 +46,9 @@ from .v2.spec.definitions import (
22
46
  ToolDefinition,
23
47
  Workflow,
24
48
  )
49
+ from .v2.validator import validate_integrity, validate_loose
25
50
 
26
- __version__ = "0.13.0"
51
+ __version__ = "0.15.0"
27
52
 
28
53
  Manifest = ManifestV2
29
54
  Recipe = ManifestV2
@@ -48,4 +73,31 @@ __all__ = [
48
73
  "InterfaceDefinition",
49
74
  "StateDefinition",
50
75
  "PolicyDefinition",
76
+ "ToolRiskLevel",
77
+ "AgentCapabilities",
78
+ "DeliveryMode",
79
+ "GovernanceConfig",
80
+ "ComplianceReport",
81
+ "ComplianceViolation",
82
+ "Identity",
83
+ "Role",
84
+ "ChatMessage",
85
+ "PresentationEventType",
86
+ "PresentationEvent",
87
+ "AnyPresentationEvent",
88
+ "CitationEvent",
89
+ "ArtifactEvent",
90
+ "HealthCheckResponse",
91
+ "HealthCheckStatus",
92
+ "ServiceRequest",
93
+ "ServiceResponse",
94
+ "StreamPacket",
95
+ "StreamError",
96
+ "StreamOpCode",
97
+ "ErrorSeverity",
98
+ "validate_integrity",
99
+ "validate_loose",
100
+ "check_compliance_v2",
101
+ "AgentRequest",
102
+ "ServiceContract",
51
103
  ]
@@ -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,28 @@
1
+ from enum import Enum
2
+ from typing import List
3
+
4
+ from pydantic import ConfigDict, Field
5
+
6
+ from ..common import CoReasonBaseModel
7
+
8
+
9
+ class DeliveryMode(str, Enum):
10
+ """Supported transport mechanisms."""
11
+
12
+ REQUEST_RESPONSE = "request_response"
13
+ SSE = "sse"
14
+
15
+
16
+ class AgentCapabilities(CoReasonBaseModel):
17
+ """Feature flags and capabilities for the agent."""
18
+
19
+ model_config = ConfigDict(frozen=True, extra="forbid")
20
+
21
+ delivery_mode: List[DeliveryMode] = Field(
22
+ default_factory=lambda: [DeliveryMode.SSE],
23
+ description="Supported transport mechanisms.",
24
+ )
25
+ history_support: bool = Field(
26
+ default=True,
27
+ description="Whether the agent supports conversation history/context.",
28
+ )
@@ -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]
@@ -0,0 +1,37 @@
1
+ from typing import Any, Dict, List, Optional
2
+
3
+ from pydantic import ConfigDict
4
+
5
+ from ..common import CoReasonBaseModel
6
+ from ..spec.cap import ServiceRequest, ServiceResponse
7
+
8
+
9
+ class AgentRequest(CoReasonBaseModel):
10
+ """Strictly typed payload inside a ServiceRequest."""
11
+
12
+ model_config = ConfigDict(frozen=True)
13
+
14
+ query: str
15
+ files: List[str] = []
16
+ conversation_id: Optional[str] = None
17
+ meta: Dict[str, Any] = {}
18
+
19
+
20
+ class ServiceContract:
21
+ """Utility class to generate the OpenAPI specification."""
22
+
23
+ @staticmethod
24
+ def generate_openapi() -> Dict[str, Any]:
25
+ """Generate the OpenAPI 3.1 Path Item Object for the agent service."""
26
+ return {
27
+ "post": {
28
+ "summary": "Invoke Agent",
29
+ "requestBody": {"content": {"application/json": {"schema": ServiceRequest.model_json_schema()}}},
30
+ "responses": {
31
+ "200": {
32
+ "description": "Successful Response",
33
+ "content": {"application/json": {"schema": ServiceResponse.model_json_schema()}},
34
+ }
35
+ },
36
+ }
37
+ }
@@ -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,13 @@
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
+ from coreason_manifest.definitions.capabilities import AgentCapabilities
6
7
  from coreason_manifest.v2.spec.contracts import InterfaceDefinition, PolicyDefinition, StateDefinition
7
8
 
8
9
 
9
- class DesignMetadata(BaseModel):
10
+ class DesignMetadata(CoReasonBaseModel):
10
11
  """UI-specific metadata for the visual builder."""
11
12
 
12
13
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -20,7 +21,7 @@ class DesignMetadata(BaseModel):
20
21
  collapsed: bool = Field(False, description="Whether the node is collapsed in UI.")
21
22
 
22
23
 
23
- class ToolDefinition(BaseModel):
24
+ class ToolDefinition(CoReasonBaseModel):
24
25
  """Definition of an external tool."""
25
26
 
26
27
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -33,7 +34,7 @@ class ToolDefinition(BaseModel):
33
34
  description: Optional[str] = Field(None, description="Description of the tool.")
34
35
 
35
36
 
36
- class AgentDefinition(BaseModel):
37
+ class AgentDefinition(CoReasonBaseModel):
37
38
  """Definition of an Agent."""
38
39
 
39
40
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -47,15 +48,18 @@ class AgentDefinition(BaseModel):
47
48
  model: Optional[str] = Field(None, description="LLM identifier.")
48
49
  tools: List[str] = Field(default_factory=list, description="List of Tool IDs or URI references.")
49
50
  knowledge: List[str] = Field(default_factory=list, description="List of file paths or knowledge base IDs.")
51
+ capabilities: AgentCapabilities = Field(
52
+ default_factory=AgentCapabilities, description="Feature flags and capabilities for the agent."
53
+ )
50
54
 
51
55
 
52
- class GenericDefinition(BaseModel):
56
+ class GenericDefinition(CoReasonBaseModel):
53
57
  """Fallback for unknown definitions."""
54
58
 
55
59
  model_config = ConfigDict(extra="allow")
56
60
 
57
61
 
58
- class BaseStep(BaseModel):
62
+ class BaseStep(CoReasonBaseModel):
59
63
  """Base attributes for all steps."""
60
64
 
61
65
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -106,7 +110,7 @@ Step = Annotated[
106
110
  ]
107
111
 
108
112
 
109
- class Workflow(BaseModel):
113
+ class Workflow(CoReasonBaseModel):
110
114
  """Defines the execution topology."""
111
115
 
112
116
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -115,7 +119,7 @@ class Workflow(BaseModel):
115
119
  steps: Dict[str, Step] = Field(..., description="Dictionary of all steps indexed by ID.")
116
120
 
117
121
 
118
- class ManifestMetadata(BaseModel):
122
+ class ManifestMetadata(CoReasonBaseModel):
119
123
  """Metadata for the manifest."""
120
124
 
121
125
  model_config = ConfigDict(extra="allow", populate_by_name=True)
@@ -124,7 +128,7 @@ class ManifestMetadata(BaseModel):
124
128
  design_metadata: Optional[DesignMetadata] = Field(None, alias="x-design", description="UI metadata.")
125
129
 
126
130
 
127
- class ManifestV2(BaseModel):
131
+ class ManifestV2(CoReasonBaseModel):
128
132
  """Root object for Coreason Manifest V2."""
129
133
 
130
134
  model_config = ConfigDict(extra="forbid", populate_by_name=True)
@@ -143,54 +147,3 @@ class ManifestV2(BaseModel):
143
147
  ],
144
148
  ] = Field(default_factory=dict, description="Reusable definitions.")
145
149
  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
@@ -13,17 +13,105 @@
13
13
  from typing import List
14
14
 
15
15
  from coreason_manifest.v2.spec.definitions import (
16
+ AgentDefinition,
17
+ AgentStep,
18
+ CouncilStep,
16
19
  ManifestV2,
17
20
  SwitchStep,
21
+ ToolDefinition,
18
22
  )
19
23
 
20
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
+
21
108
  def validate_loose(manifest: ManifestV2) -> List[str]:
22
109
  """Validate "Draft" manifests for structural sanity only.
23
110
 
24
111
  Checks:
25
112
  - Unique Step IDs.
26
113
  - SwitchStep case syntax (basic).
114
+ - Referential integrity (Start, Next, Switch, Agents, Tools) - warnings only.
27
115
 
28
116
  Args:
29
117
  manifest: The V2 manifest to validate.
@@ -38,11 +126,70 @@ def validate_loose(manifest: ManifestV2) -> List[str]:
38
126
  if step.id != step_id:
39
127
  warnings.append(f"Step key '{step_id}' does not match step.id '{step.id}'.")
40
128
 
41
- # 2. Check SwitchStep cases
129
+ # 2. Check SwitchStep cases syntax
42
130
  for step_id, step in manifest.workflow.steps.items():
43
131
  if isinstance(step, SwitchStep):
44
132
  for condition in step.cases.keys():
45
133
  if not isinstance(condition, str) or not condition.strip():
46
134
  warnings.append(f"SwitchStep '{step_id}' has invalid condition: {condition}")
47
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
+
48
195
  return warnings
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coreason_manifest
3
- Version: 0.13.0
3
+ Version: 0.16.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
 
@@ -168,6 +168,8 @@ For full details, see the [Usage Documentation](docs/usage.md).
168
168
 
169
169
  ## Documentation
170
170
 
171
+ **[Full Documentation Index](docs/index.md)**
172
+
171
173
  * [Usage Guide](docs/usage.md): How to load and create manifests.
172
174
  * [Governance & Policy Enforcement](docs/governance_policy_enforcement.md): Validating agents against organizational rules.
173
175
  * [Coreason Agent Manifest (CAM)](docs/cap/specification.md): The Canonical YAML Authoring Format.
@@ -0,0 +1,26 @@
1
+ coreason_manifest/__init__.py,sha256=zO49wgoUgYAwP7h_JoUdlJ_3vu2gxZVWP73ba8kzkv8,2588
2
+ coreason_manifest/common.py,sha256=5gdVUFR9rD0yjXu9Ao6e-71E9_qwETZLmlBt38SDGU4,2157
3
+ coreason_manifest/definitions/capabilities.py,sha256=7Ek87yB5BiMRBJWDyzrYx4zjPAAUXjB9wqLmvYDkv6g,726
4
+ coreason_manifest/definitions/identity.py,sha256=Mm_VbGY0NfKCUzjXvZwMZSI0Sk24On2Mko-yNU7YL5w,1227
5
+ coreason_manifest/definitions/message.py,sha256=CwtxeNkPmEUJO3takkg1NI0F3rfvQIrs4SoE4Dzaop8,1370
6
+ coreason_manifest/definitions/presentation.py,sha256=IrJXyuyJIuHHqcIEr0Kl_u91S7EVyrUocIu5-kdV0ek,1781
7
+ coreason_manifest/definitions/service.py,sha256=M80oa3aUERETB1QuYhpZRnPlgGXYHoYYPbM8qqFms38,1151
8
+ coreason_manifest/governance.py,sha256=Yag7uIQRhWEVJ6Gp4vK5BVSbhE1dsxPI5Rz1V6lif2k,3460
9
+ coreason_manifest/schemas/__init__.py,sha256=g1cCu1DprGcnGAiuvYWz5VeKKXIHYYwlrsqdkg9fRZY,412
10
+ coreason_manifest/schemas/coreason-v2.schema.json,sha256=R2UqXEY1NSpNNtGpyJnjJ0jhnT4U6-2FKn-WPi4tnD0,11312
11
+ coreason_manifest/spec/cap.py,sha256=3xbx3ekPdD_Py9NaqGrcillRSxdNdyp0PySg6ALdhbQ,3158
12
+ coreason_manifest/utils/__init__.py,sha256=Kdz519fMq6c7Px3MP5oD_9jfqqFbCiaHPBQTc0LhyII,848
13
+ coreason_manifest/utils/logger.py,sha256=Y9j1W0rFAh5lMAJbgMnqnyu9XmvgYdW895N5NeLvee0,461
14
+ coreason_manifest/v2/__init__.py,sha256=1bWTcGLOXjSDv2TgYXoH5wsla0T2W2ulhUHsK5AzIgM,23
15
+ coreason_manifest/v2/governance.py,sha256=HGvTIbAXXxvfuGSuUrWdoVdR9Z2UozD6F1qmmSzCm0o,6113
16
+ coreason_manifest/v2/io.py,sha256=6B5wzyW-TbnKcUgOOBaurSdZg0kQPtPx35hlcsbe1Us,4311
17
+ coreason_manifest/v2/resolver.py,sha256=7Lq58ECzW8QEnp17wpjef-LQQV2BdL74jzHgKmQMu2E,2265
18
+ coreason_manifest/v2/spec/__init__.py,sha256=i8Agxsn9vs6wGCaxaHVNupRpPCeAIGTtiA9FKyoJsaU,40
19
+ coreason_manifest/v2/spec/contracts.py,sha256=7UhE6v9ZB5I7k_6zH4NV8W_h3dtbVRMUX62XTuCROEw,1469
20
+ coreason_manifest/v2/spec/definitions.py,sha256=AyWk3hsGgA3NjmScqzC15V2_OtPlpPaHOYByMa19l4s,6438
21
+ coreason_manifest/v2/validator.py,sha256=Q2gHFbLZ95g5TCqxsvIBJanFP_UrvnIm09EKLvmYCDE,7987
22
+ coreason_manifest-0.16.0.dist-info/METADATA,sha256=x_JWaNMx8QGt3i0ihJskvHFj9eKKkLkcOJjZ2l-X_YY,8275
23
+ coreason_manifest-0.16.0.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
24
+ coreason_manifest-0.16.0.dist-info/licenses/LICENSE,sha256=3tYb7ZQe7sVXcbNmX22fDESFjOSIlCZodUGpZMkuSlk,3063
25
+ coreason_manifest-0.16.0.dist-info/licenses/NOTICE,sha256=tqzUyP9VTCGxoHLgBI0AC1i0G7m_PSyESFL8Jwuw0dA,610
26
+ coreason_manifest-0.16.0.dist-info/RECORD,,
@@ -1,20 +0,0 @@
1
- coreason_manifest/__init__.py,sha256=qy8VLZO7M4S_PWl4PB7PIztcPd_JgvKUW64S3oty0UY,1189
2
- coreason_manifest/common.py,sha256=ebQPiWiYSKFVAS6eXUyOIgJbsO8QDXVlFQKtSCQLRzg,2136
3
- coreason_manifest/governance.py,sha256=cR0q3TUbwTx2-ROGtVh9tP6o9XUiD2ekqoNsgCPDR2o,3299
4
- coreason_manifest/schemas/__init__.py,sha256=g1cCu1DprGcnGAiuvYWz5VeKKXIHYYwlrsqdkg9fRZY,412
5
- coreason_manifest/schemas/coreason-v2.schema.json,sha256=R2UqXEY1NSpNNtGpyJnjJ0jhnT4U6-2FKn-WPi4tnD0,11312
6
- coreason_manifest/utils/__init__.py,sha256=Kdz519fMq6c7Px3MP5oD_9jfqqFbCiaHPBQTc0LhyII,848
7
- coreason_manifest/utils/logger.py,sha256=Y9j1W0rFAh5lMAJbgMnqnyu9XmvgYdW895N5NeLvee0,461
8
- coreason_manifest/v2/__init__.py,sha256=1bWTcGLOXjSDv2TgYXoH5wsla0T2W2ulhUHsK5AzIgM,23
9
- coreason_manifest/v2/governance.py,sha256=nN2eu2YeASuDQglb35IOYm525LXWovMzAt9mba8loTY,5929
10
- coreason_manifest/v2/io.py,sha256=6B5wzyW-TbnKcUgOOBaurSdZg0kQPtPx35hlcsbe1Us,4311
11
- coreason_manifest/v2/resolver.py,sha256=lLPbNZcQjQ_GI71a7Rf-XiMM0CDgTy96nTLxm94gJLg,2313
12
- coreason_manifest/v2/spec/__init__.py,sha256=i8Agxsn9vs6wGCaxaHVNupRpPCeAIGTtiA9FKyoJsaU,40
13
- coreason_manifest/v2/spec/contracts.py,sha256=bDlYHDWlpSsIzDU8_fwXS2d0NjEHAPkIK3VpJbvcL7k,1354
14
- coreason_manifest/v2/spec/definitions.py,sha256=TV2JHBBq90jN18xg5sG9Y_swd1blcXyomLb1H6DkpW4,8603
15
- coreason_manifest/v2/validator.py,sha256=iTkpb_V38KbJ89h-Zne_1xep-XL44PS1PobZRnwV5K8,1504
16
- coreason_manifest-0.13.0.dist-info/METADATA,sha256=D4Hltcoz_4efVfP6PO3zG3H5gewSn5seUbOqjmCx54c,8228
17
- coreason_manifest-0.13.0.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
18
- coreason_manifest-0.13.0.dist-info/licenses/LICENSE,sha256=3tYb7ZQe7sVXcbNmX22fDESFjOSIlCZodUGpZMkuSlk,3063
19
- coreason_manifest-0.13.0.dist-info/licenses/NOTICE,sha256=tqzUyP9VTCGxoHLgBI0AC1i0G7m_PSyESFL8Jwuw0dA,610
20
- coreason_manifest-0.13.0.dist-info/RECORD,,