coreason-manifest 0.9.0__py3-none-any.whl → 0.10.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.
@@ -1,3 +1,13 @@
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
+
1
11
  # Copyright (c) 2025 CoReason, Inc.
2
12
  #
3
13
  # This software is proprietary and dual-licensed.
@@ -9,28 +19,32 @@
9
19
  # Source Code: https://github.com/CoReason-AI/coreason_maco
10
20
 
11
21
  from enum import Enum
12
- from typing import Annotated, Any, Dict, List, Literal, Optional, Union
22
+ from typing import Annotated, Any, Dict, List, Literal, Optional, Sequence, Union
23
+
24
+ from pydantic import ConfigDict, Field, StringConstraints, model_validator
13
25
 
14
- from pydantic import BaseModel, ConfigDict, Field, StringConstraints
26
+ from coreason_manifest.definitions.base import CoReasonBaseModel
15
27
 
16
28
 
17
- class StateSchema(BaseModel):
18
- """Defines the structure and persistence of the graph state.
29
+ class StateDefinition(CoReasonBaseModel):
30
+ """Defines the internal state (memory) of the Recipe.
19
31
 
20
32
  Attributes:
21
- data_schema: A JSON Schema or Pydantic definition describing the state structure.
22
- persistence: Configuration for how state is checkpointed.
33
+ schema: JSON Schema of the keys available in the shared memory.
34
+ persistence: Configuration for state durability.
23
35
  """
24
36
 
25
37
  model_config = ConfigDict(extra="forbid")
26
38
 
27
- data_schema: Dict[str, Any] = Field(
28
- ..., description="A JSON Schema or Pydantic definition describing the state structure."
39
+ schema_: Dict[str, Any] = Field(
40
+ ..., alias="schema", description="JSON Schema of the keys available in the shared memory."
41
+ )
42
+ persistence: Literal["ephemeral", "persistent"] = Field(
43
+ default="ephemeral", description="Configuration for state durability."
29
44
  )
30
- persistence: str = Field(..., description="Configuration for how state is checkpointed (e.g., 'memory', 'redis').")
31
45
 
32
46
 
33
- class CouncilConfig(BaseModel):
47
+ class CouncilConfig(CoReasonBaseModel):
34
48
  """Configuration for 'Architectural Triangulation'.
35
49
 
36
50
  Attributes:
@@ -44,7 +58,7 @@ class CouncilConfig(BaseModel):
44
58
  voters: List[str] = Field(..., description="List of agents or models that vote.")
45
59
 
46
60
 
47
- class VisualMetadata(BaseModel):
61
+ class VisualMetadata(CoReasonBaseModel):
48
62
  """Data explicitly for the UI.
49
63
 
50
64
  Attributes:
@@ -64,13 +78,28 @@ class VisualMetadata(BaseModel):
64
78
  animation_style: Optional[str] = Field(None, description="The animation style for the node.")
65
79
 
66
80
 
67
- class BaseNode(BaseModel):
81
+ class RuntimeVisualMetadata(VisualMetadata):
82
+ """Visual metadata with runtime extensions.
83
+
84
+ Attributes:
85
+ color: Color override for the node (e.g., '#00FF00').
86
+ progress: Progress indicator (e.g., '0.5' or '50%').
87
+ animation: Animation override (e.g., 'pulse').
88
+ """
89
+
90
+ color: Optional[str] = Field(None, description="Color override for the node.")
91
+ progress: Optional[str] = Field(None, description="Progress indicator.")
92
+ animation: Optional[str] = Field(None, description="Animation override.")
93
+
94
+
95
+ class BaseNode(CoReasonBaseModel):
68
96
  """Base model for all node types.
69
97
 
70
98
  Attributes:
71
99
  id: Unique identifier for the node.
72
100
  council_config: Optional configuration for architectural triangulation.
73
101
  visual: Visual metadata for the UI.
102
+ metadata: Generic metadata for operational context (e.g. cost tracking, SLAs).
74
103
  """
75
104
 
76
105
  model_config = ConfigDict(extra="forbid")
@@ -80,6 +109,10 @@ class BaseNode(BaseModel):
80
109
  None, description="Optional configuration for architectural triangulation."
81
110
  )
82
111
  visual: Optional[VisualMetadata] = Field(None, description="Visual metadata for the UI.")
112
+ metadata: Dict[str, Any] = Field(
113
+ default_factory=dict,
114
+ description="Generic metadata for operational context (e.g. cost tracking, SLAs).",
115
+ )
83
116
 
84
117
 
85
118
  class AgentNode(BaseNode):
@@ -88,10 +121,27 @@ class AgentNode(BaseNode):
88
121
  Attributes:
89
122
  type: The type of the node (must be 'agent').
90
123
  agent_name: The name of the atomic agent to call.
124
+ system_prompt: Overrides the registry default prompt. Required for ad-hoc/optimized agents.
125
+ config: Runtime-specific configuration (e.g., model parameters, temperature). Merged with registry defaults.
126
+ overrides: Runtime overrides for the agent (e.g., temperature, prompt_template_vars).
91
127
  """
92
128
 
93
129
  type: Literal["agent"] = Field("agent", description="Discriminator for AgentNode.")
94
130
  agent_name: str = Field(..., description="The name of the atomic agent to call.")
131
+ system_prompt: Optional[str] = Field(
132
+ default=None,
133
+ description="Overrides the registry default prompt. Required for ad-hoc/optimized agents.",
134
+ )
135
+ config: Optional[Dict[str, Any]] = Field(
136
+ default=None,
137
+ description=(
138
+ "Runtime-specific configuration (e.g., model parameters, temperature). Merged with registry defaults."
139
+ ),
140
+ )
141
+ overrides: Optional[Dict[str, Any]] = Field(
142
+ default=None,
143
+ description="Runtime overrides for the agent (e.g., temperature, prompt_template_vars).",
144
+ )
95
145
 
96
146
 
97
147
  class HumanNode(BaseNode):
@@ -126,7 +176,7 @@ class DataMappingStrategy(str, Enum):
126
176
  LITERAL = "literal"
127
177
 
128
178
 
129
- class DataMapping(BaseModel):
179
+ class DataMapping(CoReasonBaseModel):
130
180
  """Defines how to transform data between parent and child."""
131
181
 
132
182
  model_config = ConfigDict(extra="forbid")
@@ -178,7 +228,7 @@ Node = Annotated[
178
228
  ]
179
229
 
180
230
 
181
- class Edge(BaseModel):
231
+ class Edge(CoReasonBaseModel):
182
232
  """Represents a connection between two nodes.
183
233
 
184
234
  Attributes:
@@ -203,7 +253,7 @@ RouterRef = Annotated[
203
253
  ]
204
254
 
205
255
 
206
- class RouterExpression(BaseModel):
256
+ class RouterExpression(CoReasonBaseModel):
207
257
  """A structured expression for routing logic (e.g., CEL or JSONLogic)."""
208
258
 
209
259
  model_config = ConfigDict(extra="forbid")
@@ -218,7 +268,7 @@ RouterDefinition = Annotated[
218
268
  ]
219
269
 
220
270
 
221
- class ConditionalEdge(BaseModel):
271
+ class ConditionalEdge(CoReasonBaseModel):
222
272
  """Represents a dynamic routing connection from one node to multiple potential targets.
223
273
 
224
274
  Attributes:
@@ -236,7 +286,37 @@ class ConditionalEdge(BaseModel):
236
286
  mapping: Dict[str, str] = Field(..., description="Map of router output values to target node IDs.")
237
287
 
238
288
 
239
- class GraphTopology(BaseModel):
289
+ def validate_edge_integrity(
290
+ nodes: Sequence[Node],
291
+ edges: Sequence[Union[Edge, ConditionalEdge]],
292
+ ) -> None:
293
+ """Ensures that all edges point to valid nodes.
294
+
295
+ Args:
296
+ nodes: List of nodes in the graph.
297
+ edges: List of edges connecting the nodes.
298
+
299
+ Raises:
300
+ ValueError: If an edge points to a non-existent node.
301
+ """
302
+ node_ids = {node.id for node in nodes}
303
+
304
+ for edge in edges:
305
+ if edge.source_node_id not in node_ids:
306
+ raise ValueError(f"Edge source node '{edge.source_node_id}' not found in nodes.")
307
+
308
+ # ConditionalEdge mapping targets
309
+ if isinstance(edge, ConditionalEdge):
310
+ for target_id in edge.mapping.values():
311
+ if target_id not in node_ids:
312
+ raise ValueError(f"ConditionalEdge target node '{target_id}' not found in nodes.")
313
+ else:
314
+ # Regular Edge
315
+ if edge.target_node_id not in node_ids:
316
+ raise ValueError(f"Edge target node '{edge.target_node_id}' not found in nodes.")
317
+
318
+
319
+ class GraphTopology(CoReasonBaseModel):
240
320
  """The topology definition of the recipe.
241
321
 
242
322
  Attributes:
@@ -249,7 +329,13 @@ class GraphTopology(BaseModel):
249
329
 
250
330
  nodes: List[Node] = Field(..., description="List of nodes in the graph.")
251
331
  edges: List[Union[Edge, ConditionalEdge]] = Field(..., description="List of edges connecting the nodes.")
252
- state_schema: Optional[StateSchema] = Field(None, description="Schema definition for the graph state.")
332
+ state_schema: Optional[StateDefinition] = Field(default=None, description="Schema definition for the graph state.")
333
+
334
+ @model_validator(mode="after")
335
+ def validate_graph_integrity(self) -> "GraphTopology":
336
+ """Ensures that all edges point to valid nodes."""
337
+ validate_edge_integrity(self.nodes, self.edges)
338
+ return self
253
339
 
254
340
 
255
341
  Topology = GraphTopology
@@ -1,3 +1,13 @@
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
+
1
11
  # Copyright (c) 2025 CoReason, Inc.
2
12
  #
3
13
  # This software is proprietary and dual-licensed.
@@ -8,15 +18,16 @@
8
18
  #
9
19
  # Source Code: https://github.com/CoReason-AI/coreason_maco
10
20
 
11
- from typing import Any, Dict, Literal, Optional
21
+ from typing import Any, Dict, Optional
12
22
 
13
- from pydantic import BaseModel, ConfigDict, Field
23
+ from pydantic import ConfigDict, Field
14
24
 
15
25
  from .definitions.agent import VersionStr
16
- from .definitions.topology import GraphTopology
26
+ from .definitions.base import CoReasonBaseModel
27
+ from .definitions.topology import GraphTopology, StateDefinition
17
28
 
18
29
 
19
- class RecipeInterface(BaseModel):
30
+ class RecipeInterface(CoReasonBaseModel):
20
31
  """Defines the input/output contract for a Recipe.
21
32
 
22
33
  Attributes:
@@ -32,25 +43,7 @@ class RecipeInterface(BaseModel):
32
43
  )
33
44
 
34
45
 
35
- class StateDefinition(BaseModel):
36
- """Defines the internal state (memory) of the Recipe.
37
-
38
- Attributes:
39
- schema: JSON Schema of the keys available in the shared memory.
40
- persistence: Configuration for state durability.
41
- """
42
-
43
- model_config = ConfigDict(extra="forbid")
44
-
45
- schema_: Dict[str, Any] = Field(
46
- ..., alias="schema", description="JSON Schema of the keys available in the shared memory."
47
- )
48
- persistence: Literal["ephemeral", "persistent"] = Field(
49
- default="ephemeral", description="Configuration for state durability."
50
- )
51
-
52
-
53
- class RecipeManifest(BaseModel):
46
+ class RecipeManifest(CoReasonBaseModel):
54
47
  """The executable specification for the MACO engine.
55
48
 
56
49
  Attributes:
@@ -62,6 +55,8 @@ class RecipeManifest(BaseModel):
62
55
  state: Defines the internal state (memory) of the Recipe.
63
56
  parameters: Dictionary of build-time constants.
64
57
  topology: The topology definition of the workflow.
58
+ integrity_hash: SHA256 hash of the canonical JSON representation of the topology.
59
+ metadata: Container for design-time data.
65
60
  """
66
61
 
67
62
  model_config = ConfigDict(extra="forbid")
@@ -74,3 +69,16 @@ class RecipeManifest(BaseModel):
74
69
  state: StateDefinition = Field(..., description="Defines the internal state (memory) of the Recipe.")
75
70
  parameters: Dict[str, Any] = Field(..., description="Dictionary of build-time constants.")
76
71
  topology: GraphTopology = Field(..., description="The topology definition of the workflow.")
72
+ integrity_hash: Optional[str] = Field(
73
+ default=None,
74
+ description=(
75
+ "SHA256 hash of the canonical JSON representation of the topology. "
76
+ "Enforced by Builder, verified by Runtime."
77
+ ),
78
+ )
79
+ metadata: Dict[str, Any] = Field(
80
+ default_factory=dict,
81
+ description=(
82
+ "Container for design-time data (UI coordinates, resolution logs, draft status) to support re-hydration."
83
+ ),
84
+ )
@@ -1 +1,9 @@
1
- # Prosperity-3.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
@@ -7,7 +7,14 @@
7
7
  "tools": {
8
8
  "description": "List of MCP tool requirements.",
9
9
  "items": {
10
- "$ref": "#/$defs/ToolRequirement"
10
+ "anyOf": [
11
+ {
12
+ "$ref": "#/$defs/ToolRequirement"
13
+ },
14
+ {
15
+ "$ref": "#/$defs/InlineToolDefinition"
16
+ }
17
+ ]
11
18
  },
12
19
  "title": "Tools",
13
20
  "type": "array"
@@ -109,7 +116,7 @@
109
116
  },
110
117
  "AgentNode": {
111
118
  "additionalProperties": false,
112
- "description": "A node that calls a specific atomic agent.\n\nAttributes:\n type: The type of the node (must be 'agent').\n agent_name: The name of the atomic agent to call.",
119
+ "description": "A node that calls a specific atomic agent.\n\nAttributes:\n type: The type of the node (must be 'agent').\n agent_name: The name of the atomic agent to call.\n system_prompt: Overrides the registry default prompt. Required for ad-hoc/optimized agents.\n config: Runtime-specific configuration (e.g., model parameters, temperature). Merged with registry defaults.\n overrides: Runtime overrides for the agent (e.g., temperature, prompt_template_vars).",
113
120
  "properties": {
114
121
  "id": {
115
122
  "description": "Unique identifier for the node.",
@@ -140,6 +147,12 @@
140
147
  "default": null,
141
148
  "description": "Visual metadata for the UI."
142
149
  },
150
+ "metadata": {
151
+ "additionalProperties": true,
152
+ "description": "Generic metadata for operational context (e.g. cost tracking, SLAs).",
153
+ "title": "Metadata",
154
+ "type": "object"
155
+ },
143
156
  "type": {
144
157
  "const": "agent",
145
158
  "default": "agent",
@@ -151,6 +164,47 @@
151
164
  "description": "The name of the atomic agent to call.",
152
165
  "title": "Agent Name",
153
166
  "type": "string"
167
+ },
168
+ "system_prompt": {
169
+ "anyOf": [
170
+ {
171
+ "type": "string"
172
+ },
173
+ {
174
+ "type": "null"
175
+ }
176
+ ],
177
+ "default": null,
178
+ "description": "Overrides the registry default prompt. Required for ad-hoc/optimized agents.",
179
+ "title": "System Prompt"
180
+ },
181
+ "config": {
182
+ "anyOf": [
183
+ {
184
+ "additionalProperties": true,
185
+ "type": "object"
186
+ },
187
+ {
188
+ "type": "null"
189
+ }
190
+ ],
191
+ "default": null,
192
+ "description": "Runtime-specific configuration (e.g., model parameters, temperature). Merged with registry defaults.",
193
+ "title": "Config"
194
+ },
195
+ "overrides": {
196
+ "anyOf": [
197
+ {
198
+ "additionalProperties": true,
199
+ "type": "object"
200
+ },
201
+ {
202
+ "type": "null"
203
+ }
204
+ ],
205
+ "default": null,
206
+ "description": "Runtime overrides for the agent (e.g., temperature, prompt_template_vars).",
207
+ "title": "Overrides"
154
208
  }
155
209
  },
156
210
  "required": [
@@ -208,19 +262,37 @@
208
262
  "type": "array"
209
263
  },
210
264
  "entry_point": {
265
+ "anyOf": [
266
+ {
267
+ "type": "string"
268
+ },
269
+ {
270
+ "type": "null"
271
+ }
272
+ ],
273
+ "default": null,
211
274
  "description": "The ID of the starting node.",
212
- "title": "Entry Point",
213
- "type": "string"
275
+ "title": "Entry Point"
214
276
  },
215
277
  "model_config": {
216
278
  "$ref": "#/$defs/ModelConfig",
217
279
  "description": "Specific LLM parameters."
280
+ },
281
+ "system_prompt": {
282
+ "anyOf": [
283
+ {
284
+ "type": "string"
285
+ },
286
+ {
287
+ "type": "null"
288
+ }
289
+ ],
290
+ "default": null,
291
+ "description": "The global system prompt/instruction for the agent.",
292
+ "title": "System Prompt"
218
293
  }
219
294
  },
220
295
  "required": [
221
- "nodes",
222
- "edges",
223
- "entry_point",
224
296
  "model_config"
225
297
  ],
226
298
  "title": "AgentRuntimeConfig",
@@ -350,6 +422,12 @@
350
422
  "default": null,
351
423
  "description": "Visual metadata for the UI."
352
424
  },
425
+ "metadata": {
426
+ "additionalProperties": true,
427
+ "description": "Generic metadata for operational context (e.g. cost tracking, SLAs).",
428
+ "title": "Metadata",
429
+ "type": "object"
430
+ },
353
431
  "type": {
354
432
  "const": "human",
355
433
  "default": "human",
@@ -377,6 +455,42 @@
377
455
  "title": "HumanNode",
378
456
  "type": "object"
379
457
  },
458
+ "InlineToolDefinition": {
459
+ "additionalProperties": false,
460
+ "description": "Definition of an inline tool.\n\nAttributes:\n name: Name of the tool.\n description: Description of the tool.\n parameters: JSON Schema of parameters.\n type: The type of the tool (must be 'function').",
461
+ "properties": {
462
+ "name": {
463
+ "description": "Name of the tool.",
464
+ "title": "Name",
465
+ "type": "string"
466
+ },
467
+ "description": {
468
+ "description": "Description of the tool.",
469
+ "title": "Description",
470
+ "type": "string"
471
+ },
472
+ "parameters": {
473
+ "additionalProperties": true,
474
+ "description": "JSON Schema of parameters.",
475
+ "title": "Parameters",
476
+ "type": "object"
477
+ },
478
+ "type": {
479
+ "const": "function",
480
+ "default": "function",
481
+ "description": "The type of the tool (must be 'function').",
482
+ "title": "Type",
483
+ "type": "string"
484
+ }
485
+ },
486
+ "required": [
487
+ "name",
488
+ "description",
489
+ "parameters"
490
+ ],
491
+ "title": "InlineToolDefinition",
492
+ "type": "object"
493
+ },
380
494
  "LogicNode": {
381
495
  "additionalProperties": false,
382
496
  "description": "A node that executes pure Python logic.\n\nAttributes:\n type: The type of the node (must be 'logic').\n code: The Python logic code to execute.",
@@ -410,6 +524,12 @@
410
524
  "default": null,
411
525
  "description": "Visual metadata for the UI."
412
526
  },
527
+ "metadata": {
528
+ "additionalProperties": true,
529
+ "description": "Generic metadata for operational context (e.g. cost tracking, SLAs).",
530
+ "title": "Metadata",
531
+ "type": "object"
532
+ },
413
533
  "type": {
414
534
  "const": "logic",
415
535
  "default": "logic",
@@ -463,6 +583,12 @@
463
583
  "default": null,
464
584
  "description": "Visual metadata for the UI."
465
585
  },
586
+ "metadata": {
587
+ "additionalProperties": true,
588
+ "description": "Generic metadata for operational context (e.g. cost tracking, SLAs).",
589
+ "title": "Metadata",
590
+ "type": "object"
591
+ },
466
592
  "type": {
467
593
  "const": "map",
468
594
  "default": "map",
@@ -497,7 +623,7 @@
497
623
  },
498
624
  "ModelConfig": {
499
625
  "additionalProperties": false,
500
- "description": "LLM Configuration parameters.\n\nAttributes:\n model: The LLM model identifier.\n temperature: Temperature for generation.",
626
+ "description": "LLM Configuration parameters.\n\nAttributes:\n model: The LLM model identifier.\n temperature: Temperature for generation.\n system_prompt: The default system prompt/persona for the agent.\n persona: The full persona definition (name, description, directives).",
501
627
  "properties": {
502
628
  "model": {
503
629
  "description": "The LLM model identifier.",
@@ -510,6 +636,31 @@
510
636
  "minimum": 0.0,
511
637
  "title": "Temperature",
512
638
  "type": "number"
639
+ },
640
+ "system_prompt": {
641
+ "anyOf": [
642
+ {
643
+ "type": "string"
644
+ },
645
+ {
646
+ "type": "null"
647
+ }
648
+ ],
649
+ "default": null,
650
+ "description": "The default system prompt/persona for the agent.",
651
+ "title": "System Prompt"
652
+ },
653
+ "persona": {
654
+ "anyOf": [
655
+ {
656
+ "$ref": "#/$defs/Persona"
657
+ },
658
+ {
659
+ "type": "null"
660
+ }
661
+ ],
662
+ "default": null,
663
+ "description": "The full persona definition (name, description, directives)."
513
664
  }
514
665
  },
515
666
  "required": [
@@ -550,6 +701,37 @@
550
701
  "title": "ObservabilityConfig",
551
702
  "type": "object"
552
703
  },
704
+ "Persona": {
705
+ "additionalProperties": false,
706
+ "description": "Definition of an Agent Persona.\n\nAttributes:\n name: Name of the persona.\n description: Description of the persona.\n directives: List of specific instructions or directives.",
707
+ "properties": {
708
+ "name": {
709
+ "description": "Name of the persona.",
710
+ "title": "Name",
711
+ "type": "string"
712
+ },
713
+ "description": {
714
+ "description": "Description of the persona.",
715
+ "title": "Description",
716
+ "type": "string"
717
+ },
718
+ "directives": {
719
+ "description": "List of specific instructions or directives.",
720
+ "items": {
721
+ "type": "string"
722
+ },
723
+ "title": "Directives",
724
+ "type": "array"
725
+ }
726
+ },
727
+ "required": [
728
+ "name",
729
+ "description",
730
+ "directives"
731
+ ],
732
+ "title": "Persona",
733
+ "type": "object"
734
+ },
553
735
  "PolicyConfig": {
554
736
  "additionalProperties": false,
555
737
  "description": "Governance policy configuration.\n\nAttributes:\n budget_caps: Dictionary defining budget limits (e.g., {\"total_cost\": 10.0, \"total_tokens\": 1000}).\n human_in_the_loop: List of Node IDs that require human approval.\n allowed_domains: List of allowed domains for external access.",
@@ -615,6 +797,12 @@
615
797
  "default": null,
616
798
  "description": "Visual metadata for the UI."
617
799
  },
800
+ "metadata": {
801
+ "additionalProperties": true,
802
+ "description": "Generic metadata for operational context (e.g. cost tracking, SLAs).",
803
+ "title": "Metadata",
804
+ "type": "object"
805
+ },
618
806
  "type": {
619
807
  "const": "recipe",
620
808
  "default": "recipe",
@@ -830,6 +1018,20 @@
830
1018
  "default": null,
831
1019
  "description": "Observability configuration."
832
1020
  },
1021
+ "custom_metadata": {
1022
+ "anyOf": [
1023
+ {
1024
+ "additionalProperties": true,
1025
+ "type": "object"
1026
+ },
1027
+ {
1028
+ "type": "null"
1029
+ }
1030
+ ],
1031
+ "default": null,
1032
+ "description": "Container for arbitrary metadata extensions without breaking validation.",
1033
+ "title": "Custom Metadata"
1034
+ },
833
1035
  "integrity_hash": {
834
1036
  "description": "SHA256 hash of the source code.",
835
1037
  "pattern": "^[a-fA-F0-9]{64}$",