@m6d/cortex-server 1.1.0 → 1.1.2

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 (42) hide show
  1. package/README.md +38 -38
  2. package/dist/src/factory.d.ts +13 -1
  3. package/dist/src/ws/index.d.ts +1 -1
  4. package/package.json +54 -54
  5. package/src/adapters/database.ts +21 -28
  6. package/src/adapters/minio.ts +69 -69
  7. package/src/adapters/mssql.ts +171 -195
  8. package/src/adapters/storage.ts +4 -4
  9. package/src/ai/fetch.ts +31 -31
  10. package/src/ai/helpers.ts +18 -22
  11. package/src/ai/index.ts +101 -113
  12. package/src/ai/interceptors/resolve-captured-files.ts +42 -49
  13. package/src/ai/prompt.ts +80 -83
  14. package/src/ai/tools/call-endpoint.tool.ts +75 -82
  15. package/src/ai/tools/capture-files.tool.ts +15 -17
  16. package/src/ai/tools/execute-code.tool.ts +73 -80
  17. package/src/ai/tools/query-graph.tool.ts +17 -17
  18. package/src/auth/middleware.ts +51 -51
  19. package/src/cli/extract-endpoints.ts +436 -474
  20. package/src/config.ts +124 -134
  21. package/src/db/migrate.ts +13 -13
  22. package/src/db/migrations/20260309012148_cloudy_maria_hill/snapshot.json +303 -303
  23. package/src/db/schema.ts +46 -58
  24. package/src/factory.ts +136 -139
  25. package/src/graph/generate-cypher.ts +97 -97
  26. package/src/graph/helpers.ts +37 -37
  27. package/src/graph/index.ts +20 -20
  28. package/src/graph/neo4j.ts +82 -89
  29. package/src/graph/resolver.ts +201 -211
  30. package/src/graph/seed.ts +101 -114
  31. package/src/graph/types.ts +88 -88
  32. package/src/graph/validate.ts +55 -57
  33. package/src/index.ts +5 -5
  34. package/src/routes/chat.ts +23 -23
  35. package/src/routes/files.ts +75 -80
  36. package/src/routes/threads.ts +52 -54
  37. package/src/routes/ws.ts +22 -22
  38. package/src/types.ts +30 -30
  39. package/src/ws/connections.ts +11 -11
  40. package/src/ws/events.ts +2 -2
  41. package/src/ws/index.ts +1 -5
  42. package/src/ws/notify.ts +4 -4
package/src/graph/seed.ts CHANGED
@@ -19,154 +19,141 @@ import { validateDomain } from "./validate.ts";
19
19
  // ---------------------------------------------------------------------------
20
20
 
21
21
  export type EmbeddingConfig = {
22
- model: EmbeddingModel;
23
- dimension: number;
22
+ model: EmbeddingModel;
23
+ dimension: number;
24
24
  };
25
25
 
26
26
  export type SeedGraphConfig = {
27
- neo4j: Neo4jConfig;
28
- embedding: EmbeddingConfig;
27
+ neo4j: Neo4jConfig;
28
+ embedding: EmbeddingConfig;
29
29
  };
30
30
 
31
31
  // ---------------------------------------------------------------------------
32
32
  // Internal helpers
33
33
  // ---------------------------------------------------------------------------
34
34
 
35
- async function runStatements(
36
- client: Neo4jClient,
37
- label: string,
38
- statements: string[],
39
- ) {
40
- console.log(`\n--- ${label} (${statements.length} statements) ---`);
41
-
42
- let success = 0;
43
- let failed = 0;
44
-
45
- for (const stmt of statements) {
46
- try {
47
- const result = await client.query(stmt);
48
- const parsed = JSON.parse(result);
49
- if (parsed.error) {
50
- console.error(` ✗ Failed: ${parsed.message}`);
51
- console.error(` Statement: ${stmt.substring(0, 120)}...`);
52
- failed++;
53
- } else {
54
- success++;
55
- }
56
- } catch (e) {
57
- console.error(` ✗ Exception: ${e}`);
58
- console.error(` Statement: ${stmt.substring(0, 120)}...`);
59
- failed++;
35
+ async function runStatements(client: Neo4jClient, label: string, statements: string[]) {
36
+ console.log(`\n--- ${label} (${statements.length} statements) ---`);
37
+
38
+ let success = 0;
39
+ let failed = 0;
40
+
41
+ for (const stmt of statements) {
42
+ try {
43
+ const result = await client.query(stmt);
44
+ const parsed = JSON.parse(result);
45
+ if (parsed.error) {
46
+ console.error(` ✗ Failed: ${parsed.message}`);
47
+ console.error(` Statement: ${stmt.substring(0, 120)}...`);
48
+ failed++;
49
+ } else {
50
+ success++;
51
+ }
52
+ } catch (e) {
53
+ console.error(` ✗ Exception: ${e}`);
54
+ console.error(` Statement: ${stmt.substring(0, 120)}...`);
55
+ failed++;
56
+ }
60
57
  }
61
- }
62
58
 
63
- console.log(` ✓ ${success} succeeded, ${failed} failed`);
59
+ console.log(` ✓ ${success} succeeded, ${failed} failed`);
64
60
  }
65
61
 
66
- async function generateEmbeddings(
67
- client: Neo4jClient,
68
- embeddingConfig: EmbeddingConfig,
69
- ) {
70
- console.log("Seeding concept embeddings...");
62
+ async function generateEmbeddings(client: Neo4jClient, embeddingConfig: EmbeddingConfig) {
63
+ console.log("Seeding concept embeddings...");
71
64
 
72
- // 1. Create the vector index (idempotent)
73
- await client.query(
74
- `CREATE VECTOR INDEX concept_embeddings IF NOT EXISTS
65
+ // 1. Create the vector index (idempotent)
66
+ await client.query(
67
+ `CREATE VECTOR INDEX concept_embeddings IF NOT EXISTS
75
68
  FOR (c:Concept) ON (c.embedding)
76
69
  OPTIONS {indexConfig: {
77
70
  \`vector.dimensions\`: ${embeddingConfig.dimension},
78
71
  \`vector.similarity_function\`: 'cosine'
79
72
  }}`,
80
- );
81
- console.log("Vector index created (or already exists)");
82
-
83
- // 2. Fetch all concepts
84
- const conceptsRaw = await client.query(
85
- "MATCH (c:Concept) RETURN c.name AS name, c.description AS description",
86
- );
87
- const concepts: { name: string; description: string }[] =
88
- JSON.parse(conceptsRaw);
89
-
90
- if (!Array.isArray(concepts) || concepts.length === 0) {
91
- console.log("No concepts found in Neo4j");
92
- return;
93
- }
94
-
95
- console.log(`Found ${concepts.length} concepts, generating embeddings...`);
96
-
97
- // 3. Generate and store embeddings for each concept
98
- const embeddingResults = await Promise.all(
99
- concepts.map(async (concept) => ({
100
- concept,
101
- embedding: (
102
- await embed({
103
- model: embeddingConfig.model,
104
- value: concept.description,
105
- })
106
- ).embedding,
107
- })),
108
- );
109
-
110
- // 4. Store the embeddings in the graph
111
- await Promise.all(
112
- embeddingResults.map(
113
- async (result) =>
114
- await client.query(
115
- `MATCH (c:Concept {name: $name})
73
+ );
74
+ console.log("Vector index created (or already exists)");
75
+
76
+ // 2. Fetch all concepts
77
+ const conceptsRaw = await client.query(
78
+ "MATCH (c:Concept) RETURN c.name AS name, c.description AS description",
79
+ );
80
+ const concepts: { name: string; description: string }[] = JSON.parse(conceptsRaw);
81
+
82
+ if (!Array.isArray(concepts) || concepts.length === 0) {
83
+ console.log("No concepts found in Neo4j");
84
+ return;
85
+ }
86
+
87
+ console.log(`Found ${concepts.length} concepts, generating embeddings...`);
88
+
89
+ // 3. Generate and store embeddings for each concept
90
+ const embeddingResults = await Promise.all(
91
+ concepts.map(async (concept) => ({
92
+ concept,
93
+ embedding: (
94
+ await embed({
95
+ model: embeddingConfig.model,
96
+ value: concept.description,
97
+ })
98
+ ).embedding,
99
+ })),
100
+ );
101
+
102
+ // 4. Store the embeddings in the graph
103
+ await Promise.all(
104
+ embeddingResults.map(
105
+ async (result) =>
106
+ await client.query(
107
+ `MATCH (c:Concept {name: $name})
116
108
  SET c.embedding = $embedding`,
117
- {
118
- name: result.concept.name,
119
- embedding: result.embedding,
120
- },
109
+ {
110
+ name: result.concept.name,
111
+ embedding: result.embedding,
112
+ },
113
+ ),
121
114
  ),
122
- ),
123
- );
115
+ );
124
116
 
125
- console.log("Embedding seeding complete!");
117
+ console.log("Embedding seeding complete!");
126
118
  }
127
119
 
128
120
  // ---------------------------------------------------------------------------
129
121
  // Public API
130
122
  // ---------------------------------------------------------------------------
131
123
 
132
- export async function seedGraph(
133
- config: SeedGraphConfig,
134
- domains: Record<string, DomainDef>,
135
- ) {
136
- // Validate all domains
137
- const validationErrors = Object.entries(domains).flatMap(([name, domain]) =>
138
- validateDomain(domain).map((err) => `[${name}] ${err}`),
139
- );
140
-
141
- if (validationErrors.length) {
142
- validationErrors.forEach((err) => console.error(err));
143
- throw new Error(
144
- `Validation failed with ${validationErrors.length} error(s)`,
124
+ export async function seedGraph(config: SeedGraphConfig, domains: Record<string, DomainDef>) {
125
+ // Validate all domains
126
+ const validationErrors = Object.entries(domains).flatMap(([name, domain]) =>
127
+ validateDomain(domain).map((err) => `[${name}] ${err}`),
145
128
  );
146
- }
147
129
 
148
- const client = createNeo4jClient(config.neo4j, config.embedding.model);
149
- const targets = Object.entries(domains);
130
+ if (validationErrors.length) {
131
+ validationErrors.forEach((err) => console.error(err));
132
+ throw new Error(`Validation failed with ${validationErrors.length} error(s)`);
133
+ }
134
+
135
+ const client = createNeo4jClient(config.neo4j, config.embedding.model);
136
+ const targets = Object.entries(domains);
150
137
 
151
- console.log(`Seeding ${targets.length} domain(s)...`);
138
+ console.log(`Seeding ${targets.length} domain(s)...`);
152
139
 
153
- // Generate Cypher for all target domains
154
- const allCypher: [string, GeneratedCypher][] = targets.map(([name, data]) => [
155
- name,
156
- generateCypher(data),
157
- ]);
140
+ // Generate Cypher for all target domains
141
+ const allCypher: [string, GeneratedCypher][] = targets.map(([name, data]) => [
142
+ name,
143
+ generateCypher(data),
144
+ ]);
158
145
 
159
- // Phase 1: Create all nodes across every domain first.
160
- // This ensures cross-domain references resolve correctly.
161
- const allNodes = allCypher.flatMap(([, c]) => c.nodes);
162
- await runStatements(client, "Phase 1: Nodes", allNodes);
146
+ // Phase 1: Create all nodes across every domain first.
147
+ // This ensures cross-domain references resolve correctly.
148
+ const allNodes = allCypher.flatMap(([, c]) => c.nodes);
149
+ await runStatements(client, "Phase 1: Nodes", allNodes);
163
150
 
164
- // Phase 2: Create all edges now that every node exists.
165
- const allEdges = allCypher.flatMap(([, c]) => c.edges);
166
- await runStatements(client, "Phase 2: Edges", allEdges);
151
+ // Phase 2: Create all edges now that every node exists.
152
+ const allEdges = allCypher.flatMap(([, c]) => c.edges);
153
+ await runStatements(client, "Phase 2: Edges", allEdges);
167
154
 
168
- console.log("\nGraph seeding complete!");
155
+ console.log("\nGraph seeding complete!");
169
156
 
170
- // Phase 3: Generate embeddings for concepts
171
- await generateEmbeddings(client, config.embedding);
157
+ // Phase 3: Generate embeddings for concepts
158
+ await generateEmbeddings(client, config.embedding);
172
159
  }
@@ -12,33 +12,33 @@
12
12
  // ---------------------------------------------------------------------------
13
13
 
14
14
  export type EndpointScalarType =
15
- | "uuid"
16
- | "number"
17
- | "date"
18
- | "datetime"
19
- | "string"
20
- | "boolean"
21
- | "any"
22
- | "object";
15
+ | "uuid"
16
+ | "number"
17
+ | "date"
18
+ | "datetime"
19
+ | "string"
20
+ | "boolean"
21
+ | "any"
22
+ | "object";
23
23
 
24
24
  export type EndpointProperty = {
25
- readonly name: string;
26
- readonly required: boolean;
27
- readonly type: EndpointScalarType | string;
28
- readonly isArray?: boolean;
29
- readonly properties?: readonly EndpointProperty[];
30
- readonly description?: string;
25
+ readonly name: string;
26
+ readonly required: boolean;
27
+ readonly type: EndpointScalarType | string;
28
+ readonly isArray?: boolean;
29
+ readonly properties?: readonly EndpointProperty[];
30
+ readonly description?: string;
31
31
  };
32
32
 
33
33
  export type ResponseKind = "object" | "array" | "paginated" | "file" | "none";
34
34
 
35
35
  export type AutoGenerated = {
36
- readonly params: readonly EndpointProperty[];
37
- readonly body: readonly EndpointProperty[];
38
- readonly response: readonly EndpointProperty[];
39
- readonly successStatus: number;
40
- readonly errorStatuses: readonly number[];
41
- readonly responseKind?: ResponseKind;
36
+ readonly params: readonly EndpointProperty[];
37
+ readonly body: readonly EndpointProperty[];
38
+ readonly response: readonly EndpointProperty[];
39
+ readonly successStatus: number;
40
+ readonly errorStatuses: readonly number[];
41
+ readonly responseKind?: ResponseKind;
42
42
  };
43
43
 
44
44
  // ---------------------------------------------------------------------------
@@ -46,12 +46,12 @@ export type AutoGenerated = {
46
46
  // ---------------------------------------------------------------------------
47
47
 
48
48
  export type ConceptDef = {
49
- readonly __brand: "concept";
50
- name: string;
51
- description: string;
52
- aliases?: string[];
53
- parentConcept?: ConceptDef;
54
- governedBy?: RuleDef[];
49
+ readonly __brand: "concept";
50
+ name: string;
51
+ description: string;
52
+ aliases?: string[];
53
+ parentConcept?: ConceptDef;
54
+ governedBy?: RuleDef[];
55
55
  };
56
56
 
57
57
  // ---------------------------------------------------------------------------
@@ -59,31 +59,31 @@ export type ConceptDef = {
59
59
  // ---------------------------------------------------------------------------
60
60
 
61
61
  export type EndpointDef = {
62
- readonly __brand: "endpoint";
63
- name: string;
64
- description: string;
65
- path: string;
66
- method: "GET" | "POST" | "PUT" | "DELETE";
67
- propertiesDescriptions: Record<string, string>;
68
-
69
- params: EndpointProperty[];
70
- body: EndpointProperty[];
71
- response: EndpointProperty[];
72
- successStatus: number;
73
- errorStatuses: number[];
74
- responseKind: ResponseKind;
75
-
76
- queries?: ConceptDef[];
77
- mutates?: ConceptDef[];
78
-
79
- returns?: { concept: ConceptDef; field?: string }[];
80
- dependsOn?: {
81
- endpoint: EndpointDef;
82
- paramName: string;
83
- fromField: string;
84
- }[];
85
-
86
- governedBy?: RuleDef[];
62
+ readonly __brand: "endpoint";
63
+ name: string;
64
+ description: string;
65
+ path: string;
66
+ method: "GET" | "POST" | "PUT" | "DELETE";
67
+ propertiesDescriptions: Record<string, string>;
68
+
69
+ params: EndpointProperty[];
70
+ body: EndpointProperty[];
71
+ response: EndpointProperty[];
72
+ successStatus: number;
73
+ errorStatuses: number[];
74
+ responseKind: ResponseKind;
75
+
76
+ queries?: ConceptDef[];
77
+ mutates?: ConceptDef[];
78
+
79
+ returns?: { concept: ConceptDef; field?: string }[];
80
+ dependsOn?: {
81
+ endpoint: EndpointDef;
82
+ paramName: string;
83
+ fromField: string;
84
+ }[];
85
+
86
+ governedBy?: RuleDef[];
87
87
  };
88
88
 
89
89
  // ---------------------------------------------------------------------------
@@ -91,27 +91,27 @@ export type EndpointDef = {
91
91
  // ---------------------------------------------------------------------------
92
92
 
93
93
  export type EndpointInput = {
94
- name: string;
95
- path: string;
96
- method: "GET" | "POST" | "PUT" | "DELETE";
97
- description?: string;
98
- autoGenerated?: AutoGenerated;
99
-
100
- propertiesDescriptions?: Record<string, string>;
101
-
102
- queries?: ConceptDef[];
103
- mutates?: ConceptDef[];
104
- paramDescriptions?: Partial<Record<string, string>>;
105
- responseDescriptions?: Partial<Record<string, string>>;
106
-
107
- returns?: { concept: ConceptDef; field?: string }[];
108
- dependsOn?: {
109
- endpoint: EndpointDef;
110
- paramName: string;
111
- fromField: string;
112
- }[];
113
-
114
- governedBy?: RuleDef[];
94
+ name: string;
95
+ path: string;
96
+ method: "GET" | "POST" | "PUT" | "DELETE";
97
+ description?: string;
98
+ autoGenerated?: AutoGenerated;
99
+
100
+ propertiesDescriptions?: Record<string, string>;
101
+
102
+ queries?: ConceptDef[];
103
+ mutates?: ConceptDef[];
104
+ paramDescriptions?: Partial<Record<string, string>>;
105
+ responseDescriptions?: Partial<Record<string, string>>;
106
+
107
+ returns?: { concept: ConceptDef; field?: string }[];
108
+ dependsOn?: {
109
+ endpoint: EndpointDef;
110
+ paramName: string;
111
+ fromField: string;
112
+ }[];
113
+
114
+ governedBy?: RuleDef[];
115
115
  };
116
116
 
117
117
  // ---------------------------------------------------------------------------
@@ -119,12 +119,12 @@ export type EndpointInput = {
119
119
  // ---------------------------------------------------------------------------
120
120
 
121
121
  export type ServiceDef = {
122
- readonly __brand: "service";
123
- name: string;
124
- description: string;
125
- builtInId: string;
126
- belongsTo: ConceptDef;
127
- governedBy?: RuleDef[];
122
+ readonly __brand: "service";
123
+ name: string;
124
+ description: string;
125
+ builtInId: string;
126
+ belongsTo: ConceptDef;
127
+ governedBy?: RuleDef[];
128
128
  };
129
129
 
130
130
  // ---------------------------------------------------------------------------
@@ -132,9 +132,9 @@ export type ServiceDef = {
132
132
  // ---------------------------------------------------------------------------
133
133
 
134
134
  export type RuleDef = {
135
- readonly __brand: "rule";
136
- name: string;
137
- description: string;
135
+ readonly __brand: "rule";
136
+ name: string;
137
+ description: string;
138
138
  };
139
139
 
140
140
  // ---------------------------------------------------------------------------
@@ -142,11 +142,11 @@ export type RuleDef = {
142
142
  // ---------------------------------------------------------------------------
143
143
 
144
144
  export type DomainDef = {
145
- readonly __brand: "domain";
146
- name: string;
147
- description: string;
148
- concepts?: ConceptDef[];
149
- endpoints?: EndpointDef[];
150
- services?: ServiceDef[];
151
- rules?: RuleDef[];
145
+ readonly __brand: "domain";
146
+ name: string;
147
+ description: string;
148
+ concepts?: ConceptDef[];
149
+ endpoints?: EndpointDef[];
150
+ services?: ServiceDef[];
151
+ rules?: RuleDef[];
152
152
  };
@@ -1,80 +1,78 @@
1
1
  import type { DomainDef } from "./types.ts";
2
2
 
3
3
  export function validateDomain(data: DomainDef) {
4
- const errors: string[] = [];
4
+ const errors: string[] = [];
5
5
 
6
- const seenConceptNames = new Set<string>();
7
- for (const c of data.concepts ?? []) {
8
- if (seenConceptNames.has(c.name)) {
9
- errors.push(`Duplicate concept name: "${c.name}"`);
6
+ const seenConceptNames = new Set<string>();
7
+ for (const c of data.concepts ?? []) {
8
+ if (seenConceptNames.has(c.name)) {
9
+ errors.push(`Duplicate concept name: "${c.name}"`);
10
+ }
11
+ seenConceptNames.add(c.name);
10
12
  }
11
- seenConceptNames.add(c.name);
12
- }
13
13
 
14
- const seenEndpointNames = new Set<string>();
15
- for (const e of data.endpoints ?? []) {
16
- if (seenEndpointNames.has(e.name)) {
17
- errors.push(`Duplicate endpoint name: "${e.name}"`);
14
+ const seenEndpointNames = new Set<string>();
15
+ for (const e of data.endpoints ?? []) {
16
+ if (seenEndpointNames.has(e.name)) {
17
+ errors.push(`Duplicate endpoint name: "${e.name}"`);
18
+ }
19
+ seenEndpointNames.add(e.name);
18
20
  }
19
- seenEndpointNames.add(e.name);
20
- }
21
21
 
22
- const seenServiceNames = new Set<string>();
23
- for (const s of data.services ?? []) {
24
- if (seenServiceNames.has(s.name)) {
25
- errors.push(`Duplicate service name: "${s.name}"`);
22
+ const seenServiceNames = new Set<string>();
23
+ for (const s of data.services ?? []) {
24
+ if (seenServiceNames.has(s.name)) {
25
+ errors.push(`Duplicate service name: "${s.name}"`);
26
+ }
27
+ seenServiceNames.add(s.name);
26
28
  }
27
- seenServiceNames.add(s.name);
28
- }
29
29
 
30
- const seenRuleNames = new Set<string>();
31
- for (const r of data.rules ?? []) {
32
- if (seenRuleNames.has(r.name)) {
33
- errors.push(`Duplicate rule name: "${r.name}"`);
30
+ const seenRuleNames = new Set<string>();
31
+ for (const r of data.rules ?? []) {
32
+ if (seenRuleNames.has(r.name)) {
33
+ errors.push(`Duplicate rule name: "${r.name}"`);
34
+ }
35
+ seenRuleNames.add(r.name);
34
36
  }
35
- seenRuleNames.add(r.name);
36
- }
37
37
 
38
- // Cross-domain references are allowed (concepts/endpoints/services may
39
- // be defined in another domain), so we do not enforce local membership.
38
+ // Cross-domain references are allowed (concepts/endpoints/services may
39
+ // be defined in another domain), so we do not enforce local membership.
40
40
 
41
- const referencedConcepts = new Set<string>();
42
- const referencedRules = new Set<string>();
41
+ const referencedConcepts = new Set<string>();
42
+ const referencedRules = new Set<string>();
43
43
 
44
- for (const ep of data.endpoints ?? []) {
45
- ep.queries?.forEach((c) => referencedConcepts.add(c.name));
46
- ep.mutates?.forEach((c) => referencedConcepts.add(c.name));
47
- ep.returns?.forEach((r) => referencedConcepts.add(r.concept.name));
48
- ep.governedBy?.forEach((r) => referencedRules.add(r.name));
49
- }
44
+ for (const ep of data.endpoints ?? []) {
45
+ ep.queries?.forEach((c) => referencedConcepts.add(c.name));
46
+ ep.mutates?.forEach((c) => referencedConcepts.add(c.name));
47
+ ep.returns?.forEach((r) => referencedConcepts.add(r.concept.name));
48
+ ep.governedBy?.forEach((r) => referencedRules.add(r.name));
49
+ }
50
50
 
51
- for (const svc of data.services ?? []) {
52
- referencedConcepts.add(svc.belongsTo.name);
53
- svc.governedBy?.forEach((r) => referencedRules.add(r.name));
54
- }
51
+ for (const svc of data.services ?? []) {
52
+ referencedConcepts.add(svc.belongsTo.name);
53
+ svc.governedBy?.forEach((r) => referencedRules.add(r.name));
54
+ }
55
55
 
56
- for (const c of data.concepts ?? []) {
57
- c.governedBy?.forEach((r) => referencedRules.add(r.name));
58
- if (c.parentConcept) {
59
- referencedConcepts.add(c.parentConcept.name);
56
+ for (const c of data.concepts ?? []) {
57
+ c.governedBy?.forEach((r) => referencedRules.add(r.name));
58
+ if (c.parentConcept) {
59
+ referencedConcepts.add(c.parentConcept.name);
60
+ }
60
61
  }
61
- }
62
62
 
63
- for (const c of data.concepts ?? []) {
64
- if (!referencedConcepts.has(c.name)) {
65
- errors.push(
66
- `Concept "${c.name}" is never referenced by any endpoint, or service`,
67
- );
63
+ for (const c of data.concepts ?? []) {
64
+ if (!referencedConcepts.has(c.name)) {
65
+ errors.push(`Concept "${c.name}" is never referenced by any endpoint, or service`);
66
+ }
68
67
  }
69
- }
70
68
 
71
- for (const r of data.rules ?? []) {
72
- if (!referencedRules.has(r.name)) {
73
- errors.push(
74
- `Rule "${r.name}" is never referenced by any endpoint, service, or concept`,
75
- );
69
+ for (const r of data.rules ?? []) {
70
+ if (!referencedRules.has(r.name)) {
71
+ errors.push(
72
+ `Rule "${r.name}" is never referenced by any endpoint, service, or concept`,
73
+ );
74
+ }
76
75
  }
77
- }
78
76
 
79
- return errors;
77
+ return errors;
80
78
  }
package/src/index.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  // Config types
2
2
  export type {
3
- CortexConfig,
4
- CortexAgentDefinition,
5
- KnowledgeConfig,
6
- DatabaseConfig,
7
- StorageConfig,
3
+ CortexConfig,
4
+ CortexAgentDefinition,
5
+ KnowledgeConfig,
6
+ DatabaseConfig,
7
+ StorageConfig,
8
8
  } from "./config";
9
9
 
10
10
  // Types consumers may need