@exellix/graph-engine 6.0.0

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 (202) hide show
  1. package/.env.example +3 -0
  2. package/CHANGELOG.md +208 -0
  3. package/README.md +827 -0
  4. package/dist/src/errors/ExellixGraphError.d.ts +38 -0
  5. package/dist/src/errors/ExellixGraphError.js +21 -0
  6. package/dist/src/errors/exellixGraphErrorCodes.d.ts +31 -0
  7. package/dist/src/errors/exellixGraphErrorCodes.js +32 -0
  8. package/dist/src/index.d.ts +100 -0
  9. package/dist/src/index.js +75 -0
  10. package/dist/src/inspection/contractInspection.d.ts +21 -0
  11. package/dist/src/inspection/contractInspection.js +526 -0
  12. package/dist/src/inspection/contractTypes.d.ts +137 -0
  13. package/dist/src/inspection/contractTypes.js +1 -0
  14. package/dist/src/inspection/controlInspection.d.ts +22 -0
  15. package/dist/src/inspection/controlInspection.js +130 -0
  16. package/dist/src/inspection/graphInspection.d.ts +51 -0
  17. package/dist/src/inspection/graphInspection.js +467 -0
  18. package/dist/src/inspection/index.d.ts +21 -0
  19. package/dist/src/inspection/index.js +17 -0
  20. package/dist/src/inspection/nodeInspection.d.ts +42 -0
  21. package/dist/src/inspection/nodeInspection.js +474 -0
  22. package/dist/src/inspection/types.d.ts +321 -0
  23. package/dist/src/inspection/types.js +14 -0
  24. package/dist/src/inspection/validateAiTasksNodeExtensions.d.ts +12 -0
  25. package/dist/src/inspection/validateAiTasksNodeExtensions.js +119 -0
  26. package/dist/src/inspection/validateCatalogPlanning.d.ts +21 -0
  27. package/dist/src/inspection/validateCatalogPlanning.js +187 -0
  28. package/dist/src/integrations/ActivityTrackerIntegration.d.ts +86 -0
  29. package/dist/src/integrations/ActivityTrackerIntegration.js +134 -0
  30. package/dist/src/integrations/ActivixGraphRunIntegration.d.ts +34 -0
  31. package/dist/src/integrations/ActivixGraphRunIntegration.js +338 -0
  32. package/dist/src/integrations/ActivixNodeActivityIntegration.d.ts +33 -0
  33. package/dist/src/integrations/ActivixNodeActivityIntegration.js +220 -0
  34. package/dist/src/integrations/cataloxGraphCatalog.d.ts +21 -0
  35. package/dist/src/integrations/cataloxGraphCatalog.js +30 -0
  36. package/dist/src/integrations/createActivixExellixIntegration.d.ts +14 -0
  37. package/dist/src/integrations/createActivixExellixIntegration.js +16 -0
  38. package/dist/src/integrations/createActivixFromEnv.d.ts +31 -0
  39. package/dist/src/integrations/createActivixFromEnv.js +53 -0
  40. package/dist/src/loaders/FileGraphLoader.d.ts +23 -0
  41. package/dist/src/loaders/FileGraphLoader.js +31 -0
  42. package/dist/src/playground/PlaygroundReporter.d.ts +40 -0
  43. package/dist/src/playground/PlaygroundReporter.js +480 -0
  44. package/dist/src/playground/index.d.ts +1 -0
  45. package/dist/src/playground/index.js +1 -0
  46. package/dist/src/runtime/ExellixGraphRuntime.d.ts +263 -0
  47. package/dist/src/runtime/ExellixGraphRuntime.js +1716 -0
  48. package/dist/src/runtime/GraphEngine.d.ts +33 -0
  49. package/dist/src/runtime/GraphEngine.js +4 -0
  50. package/dist/src/runtime/aiTasksObservability.d.ts +6 -0
  51. package/dist/src/runtime/aiTasksObservability.js +37 -0
  52. package/dist/src/runtime/aiTasksStrategyPhases.d.ts +46 -0
  53. package/dist/src/runtime/aiTasksStrategyPhases.js +93 -0
  54. package/dist/src/runtime/applyAiTaskProfileWebScopingToNarrix.d.ts +17 -0
  55. package/dist/src/runtime/applyAiTaskProfileWebScopingToNarrix.js +46 -0
  56. package/dist/src/runtime/buildAiTasksRunTaskRequest.d.ts +67 -0
  57. package/dist/src/runtime/buildAiTasksRunTaskRequest.js +164 -0
  58. package/dist/src/runtime/buildRunLog.d.ts +27 -0
  59. package/dist/src/runtime/buildRunLog.js +234 -0
  60. package/dist/src/runtime/buildRunTaskTaskConfigurationForward.d.ts +9 -0
  61. package/dist/src/runtime/buildRunTaskTaskConfigurationForward.js +80 -0
  62. package/dist/src/runtime/buildTaskNodeJobContext.d.ts +11 -0
  63. package/dist/src/runtime/buildTaskNodeJobContext.js +30 -0
  64. package/dist/src/runtime/canonicalModelUsed.d.ts +6 -0
  65. package/dist/src/runtime/canonicalModelUsed.js +36 -0
  66. package/dist/src/runtime/contextualScope.d.ts +7 -0
  67. package/dist/src/runtime/contextualScope.js +121 -0
  68. package/dist/src/runtime/dataFiltersEvaluation.d.ts +60 -0
  69. package/dist/src/runtime/dataFiltersEvaluation.js +169 -0
  70. package/dist/src/runtime/deepMerge.d.ts +5 -0
  71. package/dist/src/runtime/deepMerge.js +22 -0
  72. package/dist/src/runtime/events.d.ts +92 -0
  73. package/dist/src/runtime/events.js +122 -0
  74. package/dist/src/runtime/executionMatrixHost.d.ts +98 -0
  75. package/dist/src/runtime/executionMatrixHost.js +134 -0
  76. package/dist/src/runtime/executionVariableBuckets.d.ts +67 -0
  77. package/dist/src/runtime/executionVariableBuckets.js +96 -0
  78. package/dist/src/runtime/finalizers/errors.d.ts +9 -0
  79. package/dist/src/runtime/finalizers/errors.js +10 -0
  80. package/dist/src/runtime/finalizers/executeFinalizer.d.ts +40 -0
  81. package/dist/src/runtime/finalizers/executeFinalizer.js +471 -0
  82. package/dist/src/runtime/finalizers/schema.d.ts +18 -0
  83. package/dist/src/runtime/finalizers/schema.js +63 -0
  84. package/dist/src/runtime/finalizers/validateFinalizer.d.ts +16 -0
  85. package/dist/src/runtime/finalizers/validateFinalizer.js +534 -0
  86. package/dist/src/runtime/graphDocumentFingerprint.d.ts +8 -0
  87. package/dist/src/runtime/graphDocumentFingerprint.js +21 -0
  88. package/dist/src/runtime/graphEngineMemoryPaths.d.ts +12 -0
  89. package/dist/src/runtime/graphEngineMemoryPaths.js +55 -0
  90. package/dist/src/runtime/graphResponseMapping.d.ts +23 -0
  91. package/dist/src/runtime/graphResponseMapping.js +156 -0
  92. package/dist/src/runtime/graphResponseMigration.d.ts +7 -0
  93. package/dist/src/runtime/graphResponseMigration.js +44 -0
  94. package/dist/src/runtime/graphRunExecutionSeed.d.ts +29 -0
  95. package/dist/src/runtime/graphRunExecutionSeed.js +61 -0
  96. package/dist/src/runtime/graphRunIdentity.d.ts +7 -0
  97. package/dist/src/runtime/graphRunIdentity.js +18 -0
  98. package/dist/src/runtime/localSkills/deterministicRule.d.ts +137 -0
  99. package/dist/src/runtime/localSkills/deterministicRule.js +196 -0
  100. package/dist/src/runtime/localSkills/index.d.ts +12 -0
  101. package/dist/src/runtime/localSkills/index.js +14 -0
  102. package/dist/src/runtime/localSkills/memorixItemToScopedOutput.d.ts +7 -0
  103. package/dist/src/runtime/localSkills/memorixItemToScopedOutput.js +104 -0
  104. package/dist/src/runtime/localSkills/memorixRuntime.d.ts +9 -0
  105. package/dist/src/runtime/localSkills/memorixRuntime.js +70 -0
  106. package/dist/src/runtime/localSkills/memorixScopedConfig.d.ts +16 -0
  107. package/dist/src/runtime/localSkills/memorixScopedConfig.js +18 -0
  108. package/dist/src/runtime/localSkills/scopedAnswerAssembler.d.ts +23 -0
  109. package/dist/src/runtime/localSkills/scopedAnswerAssembler.js +35 -0
  110. package/dist/src/runtime/localSkills/scopedAnswerFields.d.ts +12 -0
  111. package/dist/src/runtime/localSkills/scopedAnswerFields.js +66 -0
  112. package/dist/src/runtime/localSkills/scopedAnswerWriter.d.ts +32 -0
  113. package/dist/src/runtime/localSkills/scopedAnswerWriter.js +156 -0
  114. package/dist/src/runtime/localSkills/scopedDataReader.d.ts +47 -0
  115. package/dist/src/runtime/localSkills/scopedDataReader.js +89 -0
  116. package/dist/src/runtime/localSkills/utils.d.ts +12 -0
  117. package/dist/src/runtime/localSkills/utils.js +39 -0
  118. package/dist/src/runtime/materializeStructuredRunTaskInput.d.ts +9 -0
  119. package/dist/src/runtime/materializeStructuredRunTaskInput.js +34 -0
  120. package/dist/src/runtime/memory.d.ts +51 -0
  121. package/dist/src/runtime/memory.js +250 -0
  122. package/dist/src/runtime/mergeExellixGraphRuntimeInvocation.d.ts +18 -0
  123. package/dist/src/runtime/mergeExellixGraphRuntimeInvocation.js +32 -0
  124. package/dist/src/runtime/modelConfigSelection.d.ts +7 -0
  125. package/dist/src/runtime/modelConfigSelection.js +37 -0
  126. package/dist/src/runtime/narrixIngestEnv.d.ts +9 -0
  127. package/dist/src/runtime/narrixIngestEnv.js +18 -0
  128. package/dist/src/runtime/pathExpr.d.ts +36 -0
  129. package/dist/src/runtime/pathExpr.js +131 -0
  130. package/dist/src/runtime/predicates.d.ts +14 -0
  131. package/dist/src/runtime/predicates.js +86 -0
  132. package/dist/src/runtime/readTaskNodeInputsConfig.d.ts +23 -0
  133. package/dist/src/runtime/readTaskNodeInputsConfig.js +27 -0
  134. package/dist/src/runtime/resolveExecutionPipelineForTaskNode.d.ts +11 -0
  135. package/dist/src/runtime/resolveExecutionPipelineForTaskNode.js +93 -0
  136. package/dist/src/runtime/resolveGraphEngineMemoryPaths.d.ts +63 -0
  137. package/dist/src/runtime/resolveGraphEngineMemoryPaths.js +213 -0
  138. package/dist/src/runtime/resolveModelConfigForNode.d.ts +20 -0
  139. package/dist/src/runtime/resolveModelConfigForNode.js +69 -0
  140. package/dist/src/runtime/resolveNarrixForTaskNode.d.ts +14 -0
  141. package/dist/src/runtime/resolveNarrixForTaskNode.js +19 -0
  142. package/dist/src/runtime/resolveTaskKey.d.ts +11 -0
  143. package/dist/src/runtime/resolveTaskKey.js +28 -0
  144. package/dist/src/runtime/resolveTaskNodeInputs.d.ts +25 -0
  145. package/dist/src/runtime/resolveTaskNodeInputs.js +140 -0
  146. package/dist/src/runtime/runTaskAugments.d.ts +17 -0
  147. package/dist/src/runtime/runTaskAugments.js +37 -0
  148. package/dist/src/runtime/runTaskResponse.d.ts +4 -0
  149. package/dist/src/runtime/runTaskResponse.js +13 -0
  150. package/dist/src/runtime/runtimeObjects.d.ts +85 -0
  151. package/dist/src/runtime/runtimeObjects.js +50 -0
  152. package/dist/src/runtime/smartInputPaths.d.ts +13 -0
  153. package/dist/src/runtime/smartInputPaths.js +38 -0
  154. package/dist/src/runtime/stepRetry.d.ts +21 -0
  155. package/dist/src/runtime/stepRetry.js +238 -0
  156. package/dist/src/runtime/synthesizedContextPipeline.d.ts +12 -0
  157. package/dist/src/runtime/synthesizedContextPipeline.js +28 -0
  158. package/dist/src/runtime/taskNodeConditionsEvaluation.d.ts +27 -0
  159. package/dist/src/runtime/taskNodeConditionsEvaluation.js +140 -0
  160. package/dist/src/runtime/taskNodeMainReadiness.d.ts +45 -0
  161. package/dist/src/runtime/taskNodeMainReadiness.js +164 -0
  162. package/dist/src/runtime/taskNodeRunTaskPreflight.d.ts +89 -0
  163. package/dist/src/runtime/taskNodeRunTaskPreflight.js +204 -0
  164. package/dist/src/runtime/validateCanonicalGraphDocument.d.ts +25 -0
  165. package/dist/src/runtime/validateCanonicalGraphDocument.js +567 -0
  166. package/dist/src/runtime/variables.d.ts +2 -0
  167. package/dist/src/runtime/variables.js +1 -0
  168. package/dist/src/runtime/withTimeout.d.ts +5 -0
  169. package/dist/src/runtime/withTimeout.js +20 -0
  170. package/dist/src/types/aiTaskProfile.d.ts +41 -0
  171. package/dist/src/types/aiTaskProfile.js +6 -0
  172. package/dist/src/types/aiTasksDerivedTypes.d.ts +5 -0
  173. package/dist/src/types/aiTasksDerivedTypes.js +1 -0
  174. package/dist/src/types/events.d.ts +23 -0
  175. package/dist/src/types/events.js +1 -0
  176. package/dist/src/types/job.d.ts +9 -0
  177. package/dist/src/types/job.js +1 -0
  178. package/dist/src/types/narrix.d.ts +60 -0
  179. package/dist/src/types/narrix.js +1 -0
  180. package/dist/src/types/options.d.ts +122 -0
  181. package/dist/src/types/options.js +1 -0
  182. package/dist/src/types/refs.d.ts +747 -0
  183. package/dist/src/types/refs.js +12 -0
  184. package/dist/src/types/results.d.ts +103 -0
  185. package/dist/src/types/results.js +1 -0
  186. package/dist/src/types/runLog.d.ts +72 -0
  187. package/dist/src/types/runLog.js +18 -0
  188. package/dist/src/types/taskNodeConfiguration.d.ts +95 -0
  189. package/dist/src/types/taskNodeConfiguration.js +3 -0
  190. package/dist/src/util/packageVersion.d.ts +2 -0
  191. package/dist/src/util/packageVersion.js +12 -0
  192. package/dist/testkit/RealTasksClient.d.ts +16 -0
  193. package/dist/testkit/RealTasksClient.js +143 -0
  194. package/dist/testkit/depGraphEngineFactory.d.ts +6 -0
  195. package/dist/testkit/depGraphEngineFactory.js +54 -0
  196. package/dist/testkit/exellixRuntimeObjects.d.ts +7 -0
  197. package/dist/testkit/exellixRuntimeObjects.js +25 -0
  198. package/dist/testkit/inMemoryGraphLoader.d.ts +6 -0
  199. package/dist/testkit/inMemoryGraphLoader.js +12 -0
  200. package/dist/testkit/index.d.ts +4 -0
  201. package/dist/testkit/index.js +4 -0
  202. package/package.json +70 -0
@@ -0,0 +1,321 @@
1
+ import type { CatalogBinding, CatalogRequestEntry, GraphDocumentMetadata, GraphEdge, GraphNode, GraphPredicate, GraphReadabilityKind } from '../types/refs.js';
2
+ import type { WebScopeQuestion } from '../types/narrix.js';
3
+ import type { ScopedDataReaderOutput, ScopedDataReaderPackOutput } from '../runtime/localSkills/scopedDataReader.js';
4
+ import type { LocalSkillKey } from '../runtime/localSkills/index.js';
5
+ export type { GraphEdge, GraphPredicate, GraphReadabilityKind, WebScopeQuestion, LocalSkillKey };
6
+ export type { ScopedDataReaderOutput, ScopedDataReaderPackOutput };
7
+ /**
8
+ * The scoping question for a single node: what it asks, where it reads its data from,
9
+ * and where it writes its output to.
10
+ */
11
+ export type NodeScopingQuestion = {
12
+ nodeId: string;
13
+ /** Human-readable label (graphReadability.title). */
14
+ name: string | undefined;
15
+ /** Plain-language question or intent (graphReadability.asks || inputs.question). */
16
+ question: string | undefined;
17
+ /** Role classification of this node (graphReadability.kind). */
18
+ kind: GraphReadabilityKind | undefined;
19
+ /** Where the node sources its data from. */
20
+ sources: NodeScopeSource[];
21
+ /** Where the node writes its output to. */
22
+ targets: NodeScopeTarget[];
23
+ };
24
+ /** One data source for a node. */
25
+ export type NodeScopeSource = {
26
+ /** Category of the source. */
27
+ type: 'executionMemory' | 'outputsMemory' | 'scopedData' | 'jobMemory' | 'variables' | 'literal';
28
+ /** Dot-path in execution / job memory / variables. */
29
+ path?: string;
30
+ /** x-scoped-data scoping map id (scopedData type only). */
31
+ scopingMapId?: string;
32
+ /** Dot-path used to resolve the entity id (scopedData type only). */
33
+ entityIdPath?: string;
34
+ /** Question id for question-based scoped reads (scopedData type only). */
35
+ questionId?: string;
36
+ /** Expected answer keys from the scope map (graphReadability.reads). */
37
+ expectedKeys?: string[];
38
+ /** Human-readable label for this source (from jobContextMapping or inputs key). */
39
+ label?: string;
40
+ };
41
+ /** One output target for a node. */
42
+ export type NodeScopeTarget = {
43
+ /** Dot-path in execution/output memory where the node writes. */
44
+ path: string;
45
+ /** Target memory lane. Defaults to executionMemory for older inspection consumers. */
46
+ memory?: 'executionMemory' | 'outputsMemory';
47
+ mode?: 'replace' | 'append' | 'merge';
48
+ /** Possible output values / tier names (graphReadability.produces). */
49
+ produces?: string[];
50
+ };
51
+ /**
52
+ * Scoping question extended with live fetched x-scoped-data for a given entityId.
53
+ */
54
+ export type NodeScopingData = NodeScopingQuestion & {
55
+ entityId: string;
56
+ data: ScopedDataReaderOutput | ScopedDataReaderPackOutput | null;
57
+ error?: string;
58
+ };
59
+ /** A single execution-memory path reference with optional metadata. */
60
+ export type MemoryPath = {
61
+ path: string;
62
+ /** Memory lane for this path when known. */
63
+ memory?: 'executionMemory' | 'outputsMemory';
64
+ /** Which field or mapping this came from (for traceability). */
65
+ source?: string;
66
+ required?: boolean;
67
+ };
68
+ /**
69
+ * Conceptual execution-memory shape for a node: what it expects before running
70
+ * and what it writes after completing.
71
+ */
72
+ export type NodeMemoryShape = {
73
+ nodeId: string;
74
+ before: {
75
+ /** Paths this node reads from execution/job memory (from jobContextMapping + inputs). */
76
+ reads: MemoryPath[];
77
+ /** Paths upstream nodes in the graph are expected to have written before this node runs. */
78
+ expects: MemoryPath[];
79
+ };
80
+ after: {
81
+ /** Paths this node writes to execution memory. */
82
+ writes: MemoryPath[];
83
+ /** Paths this node writes to outputs memory. */
84
+ outputsMemoryWrites?: MemoryPath[];
85
+ };
86
+ };
87
+ /**
88
+ * Visibility into the Narrix discovery configuration of a node.
89
+ */
90
+ export type NodeNarrixDiscovery = {
91
+ nodeId: string;
92
+ /** Whether narrix is wired on this node. */
93
+ enabled: boolean;
94
+ /** Framework question id this node answers (e.g. q0, q1, q6). */
95
+ questionId?: string;
96
+ /** Passthrough of `taskConfiguration.narrix.layer` (opaque; graph / pack defines labels; engine does not interpret). */
97
+ layer?: string;
98
+ /** Narrative type ids the engine focuses discovery on. */
99
+ narrativeTypeIds?: string[];
100
+ webScope: {
101
+ /** Whether web scoping is enabled. */
102
+ enabled: boolean;
103
+ /** When true, skips DB short-circuit and always fetches from web. */
104
+ forced?: boolean;
105
+ /** Web scope questions with stable ids and purposes. */
106
+ questions?: WebScopeQuestion[];
107
+ /** Dot-path to entity id in execution memory for web scope. */
108
+ entityIdPath?: string;
109
+ /** Dot-path to entity type in execution memory for web scope. */
110
+ entityTypePath?: string;
111
+ /** Whether CVE/vendor/product tokens are appended to the web-scope question from raw input. */
112
+ enrichFromRaw?: boolean;
113
+ };
114
+ /** Fixed execution-memory paths where narrix writes its output. */
115
+ outputPaths: {
116
+ signals: 'executionMemory._narrix.scoping.signals';
117
+ stories: 'executionMemory._narrix.scoping.stories';
118
+ };
119
+ };
120
+ /**
121
+ * Full inspection of a single graph node — scoping question, memory shape,
122
+ * and narrix discovery config.
123
+ */
124
+ export type NodeInspection = {
125
+ nodeId: string;
126
+ /** "task" | "finalizer" | custom string. */
127
+ nodeType: string;
128
+ skillKey: string | undefined;
129
+ kind: GraphReadabilityKind | undefined;
130
+ scopingQuestion: NodeScopingQuestion;
131
+ memoryShape: NodeMemoryShape;
132
+ /** null when narrix is not configured on this node. */
133
+ narrixDiscovery: NodeNarrixDiscovery | null;
134
+ /** Planning-only catalog binding when present on `metadata.catalogBinding`. */
135
+ catalogBinding: CatalogBinding | undefined;
136
+ /** Planning-only single-node catalog gap when present on `metadata.catalogRequest`. */
137
+ catalogRequest: CatalogRequestEntry | undefined;
138
+ };
139
+ /**
140
+ * The I/O connection across a single graph edge: what the source node writes
141
+ * and what the destination node reads from those paths.
142
+ */
143
+ export type NodeIOEdge = {
144
+ from: string;
145
+ to: string;
146
+ /** Paths that `from` writes to either memory lane. */
147
+ writtenPaths: string[];
148
+ /** Paths that `from` writes to execution memory. */
149
+ executionWrittenPaths?: string[];
150
+ /** Paths that `from` writes to outputs memory. */
151
+ outputsWrittenPaths?: string[];
152
+ /** Paths that `to` reads from either memory lane (intersection with writtenPaths). */
153
+ readPaths: string[];
154
+ /** Paths that `to` reads from execution memory. */
155
+ executionReadPaths?: string[];
156
+ /** Paths that `to` reads from outputs memory. */
157
+ outputsReadPaths?: string[];
158
+ when?: GraphPredicate;
159
+ };
160
+ /** Synthetic node id for Layer 01 (graph entry); not executed, for inspection UIs only. */
161
+ export declare const EXELLIX_VIRTUAL_GRAPH_ENTRY_NODE_ID = "__exellixGraphEntry";
162
+ /** Synthetic node id for Layer 08 (graph response); not executed, for inspection UIs only. */
163
+ export declare const EXELLIX_VIRTUAL_GRAPH_RESPONSE_NODE_ID = "__exellixGraphResponse";
164
+ /**
165
+ * `node.metadata` flag set on nodes synthesized by {@link materializeVirtualBoundaryDiagram}.
166
+ * Executors and loaders must strip these nodes (or ignore them) before running the graph.
167
+ */
168
+ export declare const EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY: "__exellixVirtualBoundary";
169
+ /**
170
+ * Top-level property on synthetic edges from {@link materializeVirtualBoundaryDiagram}.
171
+ * @see EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY
172
+ */
173
+ export declare const EXELLIX_VIRTUAL_BOUNDARY_EDGE_FLAG_KEY: "__exellixVirtualBoundary";
174
+ export type MaterializeVirtualBoundaryDiagramOptions = {
175
+ /**
176
+ * When `true` (default), synthetic nodes include `metadata[EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY] === true`.
177
+ */
178
+ markSyntheticNodes?: boolean;
179
+ /**
180
+ * When `true` (default), synthetic edges include `edge[EXELLIX_VIRTUAL_BOUNDARY_EDGE_FLAG_KEY] === true`.
181
+ */
182
+ markSyntheticEdges?: boolean;
183
+ };
184
+ /**
185
+ * Metadata describing synthetic Layer 01 / 08 materialization; stable ids match {@link GraphVirtualIO}.
186
+ */
187
+ export type MaterializedVirtualBoundaryDiagramMeta = {
188
+ entryNodeId: typeof EXELLIX_VIRTUAL_GRAPH_ENTRY_NODE_ID;
189
+ responseNodeId: typeof EXELLIX_VIRTUAL_GRAPH_RESPONSE_NODE_ID;
190
+ firstWaveNodeIds: string[];
191
+ canonicalFinalizerId: string | null;
192
+ syntheticNodeIds: readonly [typeof EXELLIX_VIRTUAL_GRAPH_ENTRY_NODE_ID, typeof EXELLIX_VIRTUAL_GRAPH_RESPONSE_NODE_ID];
193
+ /** Synthetic nodes are never executed by exellix-graph. */
194
+ notExecutable: true;
195
+ nodeMarker: typeof EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY;
196
+ edgeMarker: typeof EXELLIX_VIRTUAL_BOUNDARY_EDGE_FLAG_KEY;
197
+ };
198
+ /**
199
+ * Deep-copied authored `nodes` / `edges` plus synthetic boundary nodes and edges.
200
+ * @see stripMaterializedVirtualBoundaryDiagram
201
+ */
202
+ export type MaterializedVirtualBoundaryDiagram = {
203
+ nodes: GraphNode[];
204
+ edges: GraphEdge[];
205
+ virtualMeta: MaterializedVirtualBoundaryDiagramMeta;
206
+ };
207
+ export type GraphVirtualIONodeRole = 'graph-entry' | 'graph-response';
208
+ /**
209
+ * A virtual boundary node mirroring platform layers 01 and 08 from `.docs/graph-io-visibility.md`.
210
+ * These are not authored in graph JSON and are not executed by the runtime.
211
+ */
212
+ export type GraphVirtualIONode = {
213
+ id: string;
214
+ role: GraphVirtualIONodeRole;
215
+ /** Cross-reference to the graph I/O visibility doc layers. */
216
+ layer: 1 | 8;
217
+ description: string;
218
+ };
219
+ export type GraphVirtualIOEdge = {
220
+ from: string;
221
+ to: string;
222
+ };
223
+ /**
224
+ * Synthesized request/response boundaries for tooling and diagrams.
225
+ * Edges: entry → each first-wave DAG node; terminal finalizer/barrier → final response (when unambiguous).
226
+ * The final response shape itself is declared at root `graph.response`.
227
+ */
228
+ export type GraphVirtualIO = {
229
+ nodes: GraphVirtualIONode[];
230
+ edges: GraphVirtualIOEdge[];
231
+ /** DAG nodes with no incoming authored edges (start after `execution.input.*` is seeded). */
232
+ firstWaveNodeIds: string[];
233
+ /**
234
+ * Single finalizer with no outgoing edges, when the graph has exactly one finalizer node.
235
+ * Otherwise null (invalid or ambiguous graph for canonical `finalOutput`).
236
+ */
237
+ canonicalFinalizerId: string | null;
238
+ };
239
+ /**
240
+ * Layer 01 anchor for diagramming — not present in graph JSON.
241
+ * @see getVirtualGraphEntryLayer
242
+ */
243
+ export type VirtualGraphEntryLayer = {
244
+ virtualNode: GraphVirtualIONode;
245
+ /** Synthetic edges from the entry anchor to each first-wave real node id. */
246
+ outgoingEdges: GraphVirtualIOEdge[];
247
+ firstWaveNodeIds: string[];
248
+ };
249
+ /**
250
+ * Layer 08 anchor for diagramming — not present in graph JSON.
251
+ * @see getVirtualGraphResponseLayer
252
+ */
253
+ export type VirtualGraphResponseLayer = {
254
+ virtualNode: GraphVirtualIONode;
255
+ /** Synthetic barrier edge from the canonical finalizer to the response anchor (empty when ambiguous). */
256
+ incomingEdges: GraphVirtualIOEdge[];
257
+ canonicalFinalizerId: string | null;
258
+ };
259
+ /**
260
+ * Full graph-level inspection: per-node inspections, execution order,
261
+ * I/O edges between nodes, and extracted catalog references.
262
+ */
263
+ export type GraphInspection = {
264
+ graphId: string;
265
+ nodes: Record<string, NodeInspection>;
266
+ /** Node ids in topological execution order. */
267
+ executionOrder: string[];
268
+ edges: GraphEdge[];
269
+ /** Per-edge I/O visibility. */
270
+ ioEdges: NodeIOEdge[];
271
+ catalogs: GraphCatalogs;
272
+ /**
273
+ * Graph `metadata` (same shape as `variables.__graphModel` during execution).
274
+ */
275
+ graphDocumentModel: GraphDocumentMetadata;
276
+ /**
277
+ * Virtual Layer 01 / 08 nodes and edges for visualization; not part of executable `nodes` / `edges`.
278
+ */
279
+ virtualIO: GraphVirtualIO;
280
+ };
281
+ /**
282
+ * All catalog references extracted from a graph JSON, aggregated across all nodes.
283
+ */
284
+ export type CatalogBindingSummary = {
285
+ nodeId: string;
286
+ catalogType: string;
287
+ catalogId: string;
288
+ version: string;
289
+ };
290
+ export type GraphCatalogs = {
291
+ /** x-scoped-data scoping map ids referenced in the graph. */
292
+ scopingMapIds: string[];
293
+ /** Narrix framework question ids referenced in the graph. */
294
+ narrixQuestionIds: string[];
295
+ /** Distinct `metadata.narrix.layer` strings on nodes (opaque metadata for tooling; not engine semantics). */
296
+ narrixLayers: string[];
297
+ /** Narrix narrative type ids referenced across all nodes. */
298
+ narrixNarrativeTypeIds: string[];
299
+ /** Local (deterministic) skill keys used in this graph. */
300
+ localSkillKeys: LocalSkillKey[];
301
+ /** AI / external skill keys used in this graph (not local skills). */
302
+ aiSkillKeys: string[];
303
+ /** Node role kinds referenced in the graph. */
304
+ graphReadabilityKinds: GraphReadabilityKind[];
305
+ /**
306
+ * Planning-only: `metadata.catalogBinding.catalogId` where `catalogType === 'scoped-question'`.
307
+ */
308
+ scopedQuestionCatalogIds: string[];
309
+ /**
310
+ * Planning-only: `metadata.catalogBinding.catalogId` where `catalogType === 'discovery-definition'`.
311
+ */
312
+ discoveryDefinitionCatalogIds: string[];
313
+ /** Per-node catalog bindings (authoring). */
314
+ catalogBindings: CatalogBindingSummary[];
315
+ /** Counts of `graph.metadata.catalogRequests` by catalog family. */
316
+ catalogRequestCounts: {
317
+ scopedQuestion: number;
318
+ discoveryDefinition: number;
319
+ other: number;
320
+ };
321
+ };
@@ -0,0 +1,14 @@
1
+ /** Synthetic node id for Layer 01 (graph entry); not executed, for inspection UIs only. */
2
+ export const EXELLIX_VIRTUAL_GRAPH_ENTRY_NODE_ID = '__exellixGraphEntry';
3
+ /** Synthetic node id for Layer 08 (graph response); not executed, for inspection UIs only. */
4
+ export const EXELLIX_VIRTUAL_GRAPH_RESPONSE_NODE_ID = '__exellixGraphResponse';
5
+ /**
6
+ * `node.metadata` flag set on nodes synthesized by {@link materializeVirtualBoundaryDiagram}.
7
+ * Executors and loaders must strip these nodes (or ignore them) before running the graph.
8
+ */
9
+ export const EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY = '__exellixVirtualBoundary';
10
+ /**
11
+ * Top-level property on synthetic edges from {@link materializeVirtualBoundaryDiagram}.
12
+ * @see EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY
13
+ */
14
+ export const EXELLIX_VIRTUAL_BOUNDARY_EDGE_FLAG_KEY = '__exellixVirtualBoundary';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Structural validation for task-node ai-tasks 7.1.x extensions (smartInput, inputSynthesis).
3
+ * Used by tooling (`validateCatalogPlanning`) and runtime (`executeNode`) with the same rules.
4
+ */
5
+ import type { TaskNode } from '../types/refs.js';
6
+ export type AiTasksNodeExtensionIssue = {
7
+ code: string;
8
+ path: string;
9
+ message: string;
10
+ };
11
+ export declare function collectAiTasksNodeExtensionIssues(node: TaskNode, nodePathPrefix: string): AiTasksNodeExtensionIssue[];
12
+ export declare function assertAiTasksNodeExtensionsValid(node: TaskNode, nodePathPrefix: string): void;
@@ -0,0 +1,119 @@
1
+ import { graphEngineMemoryPathValidationMessage, isAllowedGraphEngineMemoryPath, } from '../runtime/graphEngineMemoryPaths.js';
2
+ import { normalizeSmartInputPath } from '../runtime/smartInputPaths.js';
3
+ function isNonEmptyString(v) {
4
+ return typeof v === 'string' && v.trim().length > 0;
5
+ }
6
+ function validateMemoryPath(rawPath, issuePath, invalidCode) {
7
+ if (!isNonEmptyString(rawPath)) {
8
+ return [
9
+ {
10
+ code: invalidCode,
11
+ path: issuePath,
12
+ message: graphEngineMemoryPathValidationMessage(String(rawPath)),
13
+ },
14
+ ];
15
+ }
16
+ const trimmed = rawPath.trim();
17
+ if (!isAllowedGraphEngineMemoryPath(trimmed)) {
18
+ return [
19
+ {
20
+ code: invalidCode,
21
+ path: issuePath,
22
+ message: graphEngineMemoryPathValidationMessage(trimmed),
23
+ },
24
+ ];
25
+ }
26
+ return [];
27
+ }
28
+ export function collectAiTasksNodeExtensionIssues(node, nodePathPrefix) {
29
+ const issues = [];
30
+ const base = nodePathPrefix;
31
+ const smart = node.smartInput;
32
+ if (smart != null) {
33
+ if (!isPlainObject(smart)) {
34
+ issues.push({ code: 'SMART_INPUT_PATHS_INVALID', path: `${base}.smartInput`, message: 'smartInput must be an object' });
35
+ }
36
+ else {
37
+ const paths = smart.paths;
38
+ if (!Array.isArray(paths)) {
39
+ issues.push({
40
+ code: 'SMART_INPUT_PATHS_INVALID',
41
+ path: `${base}.smartInput.paths`,
42
+ message: 'paths must be an array of strings or { path, title?, required? } objects',
43
+ });
44
+ }
45
+ else {
46
+ for (let i = 0; i < paths.length; i++) {
47
+ const normalized = normalizeSmartInputPath(paths[i]);
48
+ if (normalized == null) {
49
+ issues.push({
50
+ code: 'SMART_INPUT_PATHS_INVALID',
51
+ path: `${base}.smartInput.paths[${i}]`,
52
+ message: 'each entry must be a non-empty string or an object with a non-empty path string',
53
+ });
54
+ continue;
55
+ }
56
+ issues.push(...validateMemoryPath(normalized, `${base}.smartInput.paths[${i}]`, 'SMART_INPUT_PATHS_INVALID'));
57
+ }
58
+ }
59
+ const strict = smart.strict;
60
+ if (strict !== undefined && typeof strict !== 'boolean') {
61
+ issues.push({ code: 'SMART_INPUT_PATHS_INVALID', path: `${base}.smartInput.strict`, message: 'strict must be a boolean when present' });
62
+ }
63
+ }
64
+ }
65
+ const profile = node.taskConfiguration?.aiTaskProfile;
66
+ const synth = profile?.inputSynthesis;
67
+ if (synth != null && synth.enabled === true) {
68
+ const dest = synth.destination;
69
+ if (dest !== 'job' && dest !== 'task' && dest !== 'execution') {
70
+ issues.push({
71
+ code: 'INPUT_SYNTHESIS_DESTINATION_INVALID',
72
+ path: `${base}.taskConfiguration.aiTaskProfile.inputSynthesis.destination`,
73
+ message: 'destination must be "job", "task", or "execution" when inputSynthesis.enabled',
74
+ });
75
+ }
76
+ if (!isNonEmptyString(synth.outputKey)) {
77
+ issues.push({
78
+ code: 'INPUT_SYNTHESIS_OUTPUT_KEY_REQUIRED',
79
+ path: `${base}.taskConfiguration.aiTaskProfile.inputSynthesis.outputKey`,
80
+ message: 'outputKey is required when inputSynthesis.enabled',
81
+ });
82
+ }
83
+ const sources = synth.sources;
84
+ if (!Array.isArray(sources) || sources.length === 0) {
85
+ issues.push({
86
+ code: 'INPUT_SYNTHESIS_SOURCES_INVALID',
87
+ path: `${base}.taskConfiguration.aiTaskProfile.inputSynthesis.sources`,
88
+ message: 'sources must be a non-empty array of strings when inputSynthesis.enabled',
89
+ });
90
+ }
91
+ else {
92
+ for (let i = 0; i < sources.length; i++) {
93
+ issues.push(...validateMemoryPath(sources[i], `${base}.taskConfiguration.aiTaskProfile.inputSynthesis.sources[${i}]`, 'INPUT_SYNTHESIS_SOURCES_INVALID'));
94
+ }
95
+ }
96
+ const mode = synth.mode;
97
+ if (mode != null && mode !== 'merge' && mode !== 'replace') {
98
+ issues.push({
99
+ code: 'INPUT_SYNTHESIS_SOURCES_INVALID',
100
+ path: `${base}.taskConfiguration.aiTaskProfile.inputSynthesis.mode`,
101
+ message: 'mode must be "merge", "replace", or omitted',
102
+ });
103
+ }
104
+ }
105
+ return issues;
106
+ }
107
+ function isPlainObject(v) {
108
+ return v != null && typeof v === 'object' && !Array.isArray(v);
109
+ }
110
+ export function assertAiTasksNodeExtensionsValid(node, nodePathPrefix) {
111
+ const issues = collectAiTasksNodeExtensionIssues(node, nodePathPrefix);
112
+ if (!issues.length)
113
+ return;
114
+ const first = issues[0];
115
+ const err = new Error(`${first.code}: ${first.message}`);
116
+ err.code = first.code;
117
+ err.issues = issues;
118
+ throw err;
119
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Structural validation for catalog planning metadata (`metadata.catalogBinding`, `metadata.catalogRequests`).
3
+ * Tooling-only — the executor does not call this and does not treat failures as runtime errors.
4
+ */
5
+ import type { CatalogBinding, CatalogRequestEntry, Graph } from '../types/refs.js';
6
+ export type CatalogPlanningValidationIssue = {
7
+ path: string;
8
+ message: string;
9
+ };
10
+ /**
11
+ * Validates catalog planning shapes on `graph.metadata` and each task node's `metadata`.
12
+ * Does not fetch external catalogs or compare to `metadata.narrix` / scoping maps.
13
+ */
14
+ export declare function validateCatalogPlanning(graph: Graph): {
15
+ ok: boolean;
16
+ errors: CatalogPlanningValidationIssue[];
17
+ };
18
+ /** Type guard: structural check only (same rules as validation). */
19
+ export declare function isCatalogBinding(value: unknown): value is CatalogBinding;
20
+ /** Type guard: structural check only (same rules as validation). */
21
+ export declare function isCatalogRequestEntry(value: unknown): value is CatalogRequestEntry;
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Structural validation for catalog planning metadata (`metadata.catalogBinding`, `metadata.catalogRequests`).
3
+ * Tooling-only — the executor does not call this and does not treat failures as runtime errors.
4
+ */
5
+ import { getGraphNodes } from './graphInspection.js';
6
+ import { assertCanonicalGraphDocument } from '../runtime/validateCanonicalGraphDocument.js';
7
+ import { collectAiTasksNodeExtensionIssues } from './validateAiTasksNodeExtensions.js';
8
+ function isNonEmptyString(v) {
9
+ return typeof v === 'string' && v.trim().length > 0;
10
+ }
11
+ function isPlainObject(v) {
12
+ return v != null && typeof v === 'object' && !Array.isArray(v);
13
+ }
14
+ function validateCatalogBinding(path, raw, errors) {
15
+ if (!isPlainObject(raw)) {
16
+ errors.push({ path, message: 'catalogBinding must be a plain object' });
17
+ return;
18
+ }
19
+ const b = raw;
20
+ if (!isNonEmptyString(b.catalogType)) {
21
+ errors.push({ path: `${path}.catalogType`, message: 'required non-empty string' });
22
+ }
23
+ if (!isNonEmptyString(b.catalogId)) {
24
+ errors.push({ path: `${path}.catalogId`, message: 'required non-empty string' });
25
+ }
26
+ if (b.version != null && typeof b.version !== 'string') {
27
+ errors.push({ path: `${path}.version`, message: 'when present, must be a string' });
28
+ }
29
+ if (b.notes != null && typeof b.notes !== 'string') {
30
+ errors.push({ path: `${path}.notes`, message: 'when present, must be a string' });
31
+ }
32
+ }
33
+ function validateCatalogRequestEntry(path, raw, errors) {
34
+ if (!isPlainObject(raw)) {
35
+ errors.push({ path, message: 'catalog request entry must be a plain object' });
36
+ return;
37
+ }
38
+ const e = raw;
39
+ if (!isNonEmptyString(e.status)) {
40
+ errors.push({ path: `${path}.status`, message: 'required non-empty string' });
41
+ }
42
+ if (!isNonEmptyString(e.catalogType)) {
43
+ errors.push({ path: `${path}.catalogType`, message: 'required non-empty string' });
44
+ }
45
+ if (!isNonEmptyString(e.requestedId)) {
46
+ errors.push({ path: `${path}.requestedId`, message: 'required non-empty string' });
47
+ }
48
+ if (e.title != null && typeof e.title !== 'string') {
49
+ errors.push({ path: `${path}.title`, message: 'when present, must be a string' });
50
+ }
51
+ if (!isNonEmptyString(e.reason)) {
52
+ errors.push({ path: `${path}.reason`, message: 'required non-empty string' });
53
+ }
54
+ if (e.requestedByNodeId != null && typeof e.requestedByNodeId !== 'string') {
55
+ errors.push({ path: `${path}.requestedByNodeId`, message: 'when present, must be a string' });
56
+ }
57
+ const ct = e.catalogType;
58
+ if (ct === 'scoped-question') {
59
+ if (!isNonEmptyString(e.title)) {
60
+ errors.push({ path: `${path}.title`, message: 'required non-empty string for catalogType scoped-question' });
61
+ }
62
+ if (!isNonEmptyString(e.asks)) {
63
+ errors.push({ path: `${path}.asks`, message: 'required non-empty string for catalogType scoped-question' });
64
+ }
65
+ if (!isNonEmptyString(e.entityType)) {
66
+ errors.push({ path: `${path}.entityType`, message: 'required non-empty string for catalogType scoped-question' });
67
+ }
68
+ if (e.proposedScopingMapId != null && typeof e.proposedScopingMapId !== 'string') {
69
+ errors.push({ path: `${path}.proposedScopingMapId`, message: 'when present, must be a string' });
70
+ }
71
+ if (e.planningNotes != null && typeof e.planningNotes !== 'string') {
72
+ errors.push({ path: `${path}.planningNotes`, message: 'when present, must be a string' });
73
+ }
74
+ return;
75
+ }
76
+ if (ct === 'discovery-definition') {
77
+ if (e.purpose != null && typeof e.purpose !== 'string') {
78
+ errors.push({ path: `${path}.purpose`, message: 'when present, must be a string' });
79
+ }
80
+ if (e.datasetId != null && typeof e.datasetId !== 'string') {
81
+ errors.push({ path: `${path}.datasetId`, message: 'when present, must be a string' });
82
+ }
83
+ if (e.layer != null && typeof e.layer !== 'string') {
84
+ errors.push({ path: `${path}.layer`, message: 'when present, must be a string' });
85
+ }
86
+ if (e.narrativeTypeIds != null) {
87
+ if (!Array.isArray(e.narrativeTypeIds)) {
88
+ errors.push({ path: `${path}.narrativeTypeIds`, message: 'when present, must be an array of strings' });
89
+ }
90
+ else {
91
+ for (let i = 0; i < e.narrativeTypeIds.length; i++) {
92
+ if (typeof e.narrativeTypeIds[i] !== 'string') {
93
+ errors.push({ path: `${path}.narrativeTypeIds[${i}]`, message: 'each narrativeTypeId must be a string' });
94
+ }
95
+ }
96
+ }
97
+ }
98
+ if (e.enableWebScope != null && typeof e.enableWebScope !== 'boolean') {
99
+ errors.push({ path: `${path}.enableWebScope`, message: 'when present, must be a boolean' });
100
+ }
101
+ if (e.discoveryNotes != null && typeof e.discoveryNotes !== 'string') {
102
+ errors.push({ path: `${path}.discoveryNotes`, message: 'when present, must be a string' });
103
+ }
104
+ if (e.expectedSemanticOutputs != null) {
105
+ if (!Array.isArray(e.expectedSemanticOutputs)) {
106
+ errors.push({ path: `${path}.expectedSemanticOutputs`, message: 'when present, must be an array of strings' });
107
+ }
108
+ else {
109
+ for (let i = 0; i < e.expectedSemanticOutputs.length; i++) {
110
+ if (typeof e.expectedSemanticOutputs[i] !== 'string') {
111
+ errors.push({ path: `${path}.expectedSemanticOutputs[${i}]`, message: 'each entry must be a string' });
112
+ }
113
+ }
114
+ }
115
+ }
116
+ if (e.compatibleTaskTypes != null) {
117
+ if (!Array.isArray(e.compatibleTaskTypes)) {
118
+ errors.push({ path: `${path}.compatibleTaskTypes`, message: 'when present, must be an array of strings' });
119
+ }
120
+ else {
121
+ for (let i = 0; i < e.compatibleTaskTypes.length; i++) {
122
+ if (typeof e.compatibleTaskTypes[i] !== 'string') {
123
+ errors.push({ path: `${path}.compatibleTaskTypes[${i}]`, message: 'each entry must be a string' });
124
+ }
125
+ }
126
+ }
127
+ }
128
+ return;
129
+ }
130
+ errors.push({
131
+ path: `${path}.catalogType`,
132
+ message: `unsupported catalogType "${ct}" (expected "scoped-question" or "discovery-definition")`,
133
+ });
134
+ }
135
+ /**
136
+ * Validates catalog planning shapes on `graph.metadata` and each task node's `metadata`.
137
+ * Does not fetch external catalogs or compare to `metadata.narrix` / scoping maps.
138
+ */
139
+ export function validateCatalogPlanning(graph) {
140
+ assertCanonicalGraphDocument(graph, { graphId: graph?.id });
141
+ const errors = [];
142
+ const gm = graph.metadata;
143
+ if (gm != null && typeof gm === 'object' && !Array.isArray(gm)) {
144
+ const cr = gm.catalogRequests;
145
+ if (cr != null) {
146
+ if (!Array.isArray(cr)) {
147
+ errors.push({ path: 'metadata.catalogRequests', message: 'must be an array when present' });
148
+ }
149
+ else {
150
+ cr.forEach((entry, i) => {
151
+ validateCatalogRequestEntry(`metadata.catalogRequests[${i}]`, entry, errors);
152
+ });
153
+ }
154
+ }
155
+ }
156
+ const nodes = getGraphNodes(graph);
157
+ for (const node of nodes) {
158
+ if (node.type === 'finalizer')
159
+ continue;
160
+ const tn = node;
161
+ const meta = tn.metadata;
162
+ if (meta != null) {
163
+ if (meta.catalogBinding != null) {
164
+ validateCatalogBinding(`nodes[${tn.id}].metadata.catalogBinding`, meta.catalogBinding, errors);
165
+ }
166
+ if (meta.catalogRequest != null) {
167
+ validateCatalogRequestEntry(`nodes[${tn.id}].metadata.catalogRequest`, meta.catalogRequest, errors);
168
+ }
169
+ }
170
+ for (const ext of collectAiTasksNodeExtensionIssues(tn, `nodes[${tn.id}]`)) {
171
+ errors.push({ path: ext.path, message: `${ext.code}: ${ext.message}` });
172
+ }
173
+ }
174
+ return { ok: errors.length === 0, errors };
175
+ }
176
+ /** Type guard: structural check only (same rules as validation). */
177
+ export function isCatalogBinding(value) {
178
+ const e = [];
179
+ validateCatalogBinding('', value, e);
180
+ return e.length === 0;
181
+ }
182
+ /** Type guard: structural check only (same rules as validation). */
183
+ export function isCatalogRequestEntry(value) {
184
+ const e = [];
185
+ validateCatalogRequestEntry('', value, e);
186
+ return e.length === 0;
187
+ }