@m6d/cortex-server 1.7.0 → 2.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 (239) hide show
  1. package/README.md +8 -3
  2. package/contracts/README.md +23 -0
  3. package/contracts/graph/embed.ts +50 -0
  4. package/{src → contracts}/graph/helpers.ts +4 -10
  5. package/{src → contracts}/graph/neo4j.ts +14 -27
  6. package/contracts/graph/schema.ts +65 -0
  7. package/{src → contracts}/graph/types.ts +2 -29
  8. package/contracts/graph.ts +36 -0
  9. package/contracts/runtime.ts +208 -0
  10. package/contracts/wire.ts +143 -0
  11. package/dist/contracts/graph/embed.d.ts +22 -0
  12. package/dist/{src → contracts}/graph/helpers.d.ts +14 -12
  13. package/dist/{src → contracts}/graph/neo4j.d.ts +5 -5
  14. package/dist/contracts/graph/schema.d.ts +62 -0
  15. package/dist/{src → contracts}/graph/types.d.ts +1 -1
  16. package/dist/contracts/graph.d.ts +14 -0
  17. package/dist/contracts/runtime.d.ts +278 -0
  18. package/dist/contracts/wire.d.ts +120 -0
  19. package/dist/index.d.ts +1 -1
  20. package/dist/src/lib/adapters/database/index.d.ts +61 -0
  21. package/dist/src/lib/adapters/database/message-content.d.ts +30 -0
  22. package/dist/src/lib/adapters/database/mssql/attachments.d.ts +100 -0
  23. package/dist/src/lib/adapters/database/mssql/client.d.ts +8 -0
  24. package/dist/src/lib/adapters/database/mssql/index.d.ts +194 -0
  25. package/dist/src/lib/adapters/database/mssql/llm-requests.d.ts +17 -0
  26. package/dist/src/lib/adapters/database/mssql/messages.d.ts +37 -0
  27. package/dist/src/lib/adapters/database/mssql/threads.d.ts +50 -0
  28. package/dist/src/lib/adapters/database/postgres/attachments.d.ts +113 -0
  29. package/dist/src/lib/adapters/database/postgres/client.d.ts +8 -0
  30. package/dist/src/lib/adapters/database/postgres/index.d.ts +207 -0
  31. package/dist/src/lib/adapters/database/postgres/llm-requests.d.ts +17 -0
  32. package/dist/src/lib/adapters/database/postgres/messages.d.ts +37 -0
  33. package/dist/src/lib/adapters/database/postgres/threads.d.ts +50 -0
  34. package/{src/adapters/storage.ts → dist/src/lib/adapters/storage/index.d.ts} +1 -1
  35. package/dist/src/{adapters → lib/adapters/storage}/minio.d.ts +1 -1
  36. package/dist/src/lib/ai/active-runs.d.ts +63 -0
  37. package/dist/src/lib/ai/attachments.d.ts +30 -0
  38. package/dist/src/lib/ai/backend-url.d.ts +16 -0
  39. package/dist/src/lib/ai/cc-runtime.d.ts +61 -0
  40. package/dist/src/lib/ai/commit-gate.d.ts +21 -0
  41. package/dist/src/{ai → lib/ai}/context/builder.d.ts +6 -7
  42. package/dist/src/lib/ai/context/compression-middleware.d.ts +15 -0
  43. package/dist/src/lib/ai/context/compressor.d.ts +7 -0
  44. package/dist/src/lib/ai/context/intra-turn-compressor.d.ts +24 -0
  45. package/dist/src/lib/ai/context/optimize-thread-context.d.ts +9 -0
  46. package/dist/src/lib/ai/context/summarizer.d.ts +5 -0
  47. package/dist/src/{ai → lib/ai}/context/token-estimator.d.ts +5 -8
  48. package/dist/src/{ai → lib/ai}/context/types.d.ts +2 -0
  49. package/dist/src/{ai → lib/ai}/fetch.d.ts +1 -1
  50. package/dist/src/lib/ai/finish-turn.d.ts +23 -0
  51. package/dist/src/lib/ai/helpers.d.ts +38 -0
  52. package/dist/src/lib/ai/index.d.ts +7 -0
  53. package/dist/src/lib/ai/inspector.d.ts +41 -0
  54. package/dist/src/lib/ai/interceptors/request-interceptor.d.ts +13 -0
  55. package/dist/src/{ai → lib/ai}/prompt.d.ts +5 -4
  56. package/dist/src/lib/ai/redis-runs.d.ts +41 -0
  57. package/dist/src/lib/ai/redis-stream-log.d.ts +39 -0
  58. package/dist/src/lib/ai/tools/execute-code.tool.d.ts +18 -0
  59. package/dist/src/lib/ai/tools/query-graph.tool.d.ts +13 -0
  60. package/dist/src/lib/ai/tools/read-attachment.tool.d.ts +21 -0
  61. package/dist/src/lib/ai/tools/search-common.d.ts +17 -0
  62. package/dist/src/lib/ai/tools/search-knowledge.tool.d.ts +15 -0
  63. package/dist/src/lib/ai/tools/search-services.tool.d.ts +12 -0
  64. package/dist/src/lib/ai/tools/search-tools.tool.d.ts +12 -0
  65. package/dist/src/lib/ai/turn-tools.d.ts +39 -0
  66. package/dist/src/lib/ai/vision/rasterize-pdf.d.ts +26 -0
  67. package/dist/src/lib/ai/vision/rasterize-pdf.worker.d.ts +1 -0
  68. package/dist/src/lib/ai/vision/vision-reader.d.ts +45 -0
  69. package/dist/src/lib/cc/client.d.ts +183 -0
  70. package/dist/src/lib/cc/config-cache.d.ts +20 -0
  71. package/dist/src/lib/cc/format.d.ts +23 -0
  72. package/dist/src/lib/cc/registry.d.ts +34 -0
  73. package/dist/src/lib/cc/types.d.ts +27 -0
  74. package/dist/src/lib/config.d.ts +170 -0
  75. package/dist/src/lib/db/drizzle.config.mssql.d.ts +2 -0
  76. package/dist/src/lib/db/drizzle.config.pg.d.ts +2 -0
  77. package/dist/src/lib/db/migrate.d.ts +7 -0
  78. package/dist/src/{db/schema.d.ts → lib/db/schema.mssql.d.ts} +236 -24
  79. package/dist/src/lib/db/schema.pg.d.ts +546 -0
  80. package/dist/src/{factory.d.ts → lib/factory.d.ts} +2 -12
  81. package/dist/src/lib/graph/index.d.ts +10 -0
  82. package/dist/src/{graph → lib/graph}/resolver.d.ts +2 -3
  83. package/dist/src/{index.d.ts → lib/index.d.ts} +3 -4
  84. package/dist/src/lib/redis.d.ts +19 -0
  85. package/dist/src/{routes → lib/routes}/chat.d.ts +1 -1
  86. package/dist/src/{routes → lib/routes}/files.d.ts +1 -1
  87. package/dist/src/lib/routes/owned-thread.d.ts +17 -0
  88. package/dist/src/{routes → lib/routes}/threads.d.ts +1 -1
  89. package/dist/src/{routes → lib/routes}/ws.d.ts +1 -1
  90. package/dist/src/lib/types.d.ts +58 -0
  91. package/dist/src/{ws → lib/ws}/connections.d.ts +2 -1
  92. package/index.ts +1 -1
  93. package/package.json +35 -15
  94. package/src/lib/adapters/database/index.ts +78 -0
  95. package/src/lib/adapters/database/message-content.ts +39 -0
  96. package/src/lib/adapters/database/mssql/attachments.ts +90 -0
  97. package/src/lib/adapters/database/mssql/client.ts +11 -0
  98. package/src/lib/adapters/database/mssql/index.ts +23 -0
  99. package/src/lib/adapters/database/mssql/llm-requests.ts +43 -0
  100. package/src/lib/adapters/database/mssql/messages.ts +92 -0
  101. package/src/lib/adapters/database/mssql/threads.ts +83 -0
  102. package/src/lib/adapters/database/postgres/attachments.ts +88 -0
  103. package/src/lib/adapters/database/postgres/client.ts +11 -0
  104. package/src/lib/adapters/database/postgres/index.ts +23 -0
  105. package/src/lib/adapters/database/postgres/llm-requests.ts +45 -0
  106. package/src/lib/adapters/database/postgres/messages.ts +92 -0
  107. package/src/lib/adapters/database/postgres/threads.ts +84 -0
  108. package/{dist/src/adapters/storage.d.ts → src/lib/adapters/storage/index.ts} +1 -1
  109. package/src/{adapters → lib/adapters/storage}/minio.ts +7 -6
  110. package/src/lib/ai/active-runs.ts +157 -0
  111. package/src/lib/ai/attachments.ts +144 -0
  112. package/src/lib/ai/backend-url.ts +27 -0
  113. package/src/lib/ai/cc-runtime.ts +63 -0
  114. package/src/lib/ai/commit-gate.ts +116 -0
  115. package/src/{ai → lib/ai}/context/builder.ts +12 -14
  116. package/src/lib/ai/context/compression-middleware.ts +44 -0
  117. package/src/lib/ai/context/compressor.ts +267 -0
  118. package/src/lib/ai/context/intra-turn-compressor.ts +181 -0
  119. package/src/lib/ai/context/optimize-thread-context.ts +50 -0
  120. package/src/lib/ai/context/summarizer.ts +48 -0
  121. package/src/lib/ai/context/token-estimator.ts +65 -0
  122. package/src/{ai → lib/ai}/context/types.ts +4 -1
  123. package/src/{ai → lib/ai}/fetch.ts +10 -5
  124. package/src/lib/ai/finish-turn.ts +75 -0
  125. package/src/lib/ai/helpers.ts +100 -0
  126. package/src/lib/ai/index.ts +408 -0
  127. package/src/lib/ai/inspector.ts +112 -0
  128. package/src/lib/ai/interceptors/request-interceptor.ts +89 -0
  129. package/src/lib/ai/prompt.ts +355 -0
  130. package/src/lib/ai/redis-runs.ts +167 -0
  131. package/src/lib/ai/redis-stream-log.ts +219 -0
  132. package/src/lib/ai/tools/execute-code.tool.ts +267 -0
  133. package/src/{ai → lib/ai}/tools/query-graph.tool.ts +7 -8
  134. package/src/lib/ai/tools/read-attachment.tool.ts +59 -0
  135. package/src/lib/ai/tools/search-common.ts +15 -0
  136. package/src/lib/ai/tools/search-knowledge.tool.ts +33 -0
  137. package/src/lib/ai/tools/search-services.tool.ts +31 -0
  138. package/src/lib/ai/tools/search-tools.tool.ts +29 -0
  139. package/src/lib/ai/turn-tools.ts +154 -0
  140. package/src/lib/ai/vision/rasterize-pdf.ts +69 -0
  141. package/src/lib/ai/vision/rasterize-pdf.worker.ts +81 -0
  142. package/src/lib/ai/vision/vision-reader.ts +130 -0
  143. package/src/{auth → lib/auth}/middleware.ts +12 -3
  144. package/src/lib/cc/client.ts +273 -0
  145. package/src/lib/cc/config-cache.ts +32 -0
  146. package/src/lib/cc/format.ts +96 -0
  147. package/src/lib/cc/registry.ts +62 -0
  148. package/src/lib/cc/types.ts +44 -0
  149. package/src/lib/config.ts +212 -0
  150. package/src/lib/db/drizzle.config.mssql.ts +9 -0
  151. package/src/lib/db/drizzle.config.pg.ts +9 -0
  152. package/src/lib/db/migrate.ts +56 -0
  153. package/src/{db/migrations/20260309012148_cloudy_maria_hill → lib/db/migrations/mssql/20260326231647_nice_speedball}/migration.sql +12 -0
  154. package/src/{db/migrations/20260309012148_cloudy_maria_hill → lib/db/migrations/mssql/20260326231647_nice_speedball}/snapshot.json +106 -1
  155. package/src/lib/db/migrations/mssql/20260729092726_talented_wilson_fisk/migration.sql +20 -0
  156. package/src/lib/db/migrations/mssql/20260729092726_talented_wilson_fisk/snapshot.json +497 -0
  157. package/src/lib/db/migrations/mssql/20260801003652_perpetual_blue_shield/migration.sql +1 -0
  158. package/src/lib/db/migrations/mssql/20260801003652_perpetual_blue_shield/snapshot.json +516 -0
  159. package/src/lib/db/migrations/mssql/20260812174642_user_id_text/migration.sql +2 -0
  160. package/src/lib/db/migrations/mssql/20260812174642_user_id_text/snapshot.json +516 -0
  161. package/src/lib/db/migrations/pg/20260801084117_thick_ben_grimm/migration.sql +53 -0
  162. package/src/lib/db/migrations/pg/20260801084117_thick_ben_grimm/snapshot.json +584 -0
  163. package/src/lib/db/migrations/pg/20260812174622_user_id_text/migration.sql +2 -0
  164. package/src/lib/db/migrations/pg/20260812174622_user_id_text/snapshot.json +584 -0
  165. package/src/lib/db/schema.mssql.ts +94 -0
  166. package/src/lib/db/schema.pg.ts +95 -0
  167. package/src/lib/factory.ts +127 -0
  168. package/src/lib/graph/index.ts +19 -0
  169. package/src/lib/graph/resolver.ts +387 -0
  170. package/src/{index.ts → lib/index.ts} +13 -5
  171. package/src/lib/redis.ts +44 -0
  172. package/src/lib/routes/chat.ts +115 -0
  173. package/src/lib/routes/files.ts +129 -0
  174. package/src/lib/routes/owned-thread.ts +17 -0
  175. package/src/{routes → lib/routes}/threads.ts +22 -31
  176. package/src/{routes → lib/routes}/ws.ts +9 -3
  177. package/src/lib/types.ts +76 -0
  178. package/src/{ws → lib/ws}/connections.ts +9 -1
  179. package/tsconfig.json +48 -0
  180. package/dist/src/adapters/database.d.ts +0 -30
  181. package/dist/src/adapters/mssql.d.ts +0 -3
  182. package/dist/src/ai/active-streams.d.ts +0 -14
  183. package/dist/src/ai/context/compressor.d.ts +0 -7
  184. package/dist/src/ai/context/index.d.ts +0 -15
  185. package/dist/src/ai/context/summarizer.d.ts +0 -5
  186. package/dist/src/ai/helpers.d.ts +0 -5
  187. package/dist/src/ai/index.d.ts +0 -4
  188. package/dist/src/ai/interceptors/request-interceptor.d.ts +0 -12
  189. package/dist/src/ai/tools/call-endpoint.tool.d.ts +0 -7
  190. package/dist/src/ai/tools/capture-files.tool.d.ts +0 -6
  191. package/dist/src/ai/tools/execute-code.tool.d.ts +0 -4
  192. package/dist/src/ai/tools/query-graph.tool.d.ts +0 -5
  193. package/dist/src/cli/extract-endpoints.d.ts +0 -6
  194. package/dist/src/config.d.ts +0 -165
  195. package/dist/src/db/migrate.d.ts +0 -1
  196. package/dist/src/graph/expand-domains.d.ts +0 -2
  197. package/dist/src/graph/generate-cypher.d.ts +0 -22
  198. package/dist/src/graph/index.d.ts +0 -11
  199. package/dist/src/graph/seed.d.ts +0 -19
  200. package/dist/src/graph/validate.d.ts +0 -2
  201. package/dist/src/routes/index.d.ts +0 -4
  202. package/dist/src/types.d.ts +0 -74
  203. package/dist/src/ws/events.d.ts +0 -39
  204. package/dist/src/ws/index.d.ts +0 -3
  205. package/dist/src/ws/notify.d.ts +0 -2
  206. package/src/adapters/database.ts +0 -29
  207. package/src/adapters/mssql.ts +0 -195
  208. package/src/ai/active-streams.ts +0 -123
  209. package/src/ai/context/compressor.ts +0 -47
  210. package/src/ai/context/index.ts +0 -75
  211. package/src/ai/context/summarizer.ts +0 -50
  212. package/src/ai/context/token-estimator.ts +0 -60
  213. package/src/ai/helpers.ts +0 -32
  214. package/src/ai/index.ts +0 -277
  215. package/src/ai/interceptors/request-interceptor.ts +0 -61
  216. package/src/ai/prompt.ts +0 -126
  217. package/src/ai/tools/call-endpoint.tool.ts +0 -89
  218. package/src/ai/tools/capture-files.tool.ts +0 -20
  219. package/src/ai/tools/execute-code.tool.ts +0 -153
  220. package/src/cli/extract-endpoints.ts +0 -550
  221. package/src/config.ts +0 -182
  222. package/src/db/migrate.ts +0 -21
  223. package/src/db/migrations/20260315000000_add_context_meta/migration.sql +0 -1
  224. package/src/db/schema.ts +0 -70
  225. package/src/factory.ts +0 -171
  226. package/src/graph/expand-domains.ts +0 -276
  227. package/src/graph/generate-cypher.ts +0 -192
  228. package/src/graph/index.ts +0 -47
  229. package/src/graph/resolver.ts +0 -357
  230. package/src/graph/seed.ts +0 -162
  231. package/src/graph/validate.ts +0 -78
  232. package/src/routes/chat.ts +0 -86
  233. package/src/routes/files.ts +0 -100
  234. package/src/routes/index.ts +0 -4
  235. package/src/types.ts +0 -74
  236. package/src/ws/events.ts +0 -39
  237. package/src/ws/index.ts +0 -11
  238. package/src/ws/notify.ts +0 -9
  239. /package/dist/src/{auth → lib/auth}/middleware.d.ts +0 -0
@@ -1,357 +0,0 @@
1
- /**
2
- * Pre-resolves relevant API endpoints from the Neo4j knowledge graph
3
- * using vector similarity search on concept descriptions.
4
- *
5
- * All configuration is passed explicitly — no hardcoded model references
6
- * or environment variables.
7
- */
8
-
9
- import type { EmbeddingModel } from "ai";
10
- import { embed } from "ai";
11
- import type { Neo4jClient } from "./neo4j.ts";
12
- import type { ConceptDef } from "./types.ts";
13
-
14
- // ---------------------------------------------------------------------------
15
- // Config
16
- // ---------------------------------------------------------------------------
17
-
18
- export type RerankerConfig = {
19
- url: string;
20
- apiKey: string;
21
- };
22
-
23
- export type ResolverConfig = {
24
- neo4j: Neo4jClient;
25
- embeddingModel: EmbeddingModel;
26
- reranker?: RerankerConfig;
27
- };
28
-
29
- // ---------------------------------------------------------------------------
30
- // Result types
31
- // ---------------------------------------------------------------------------
32
-
33
- type EndpointDependency = {
34
- depPath: string;
35
- depMethod: string;
36
- paramName: string;
37
- fromField: string;
38
- };
39
-
40
- type EndpointRelation = "read" | "write";
41
-
42
- export type ResolvedEndpoint = {
43
- concept: string;
44
- relation: EndpointRelation;
45
- name: string;
46
- path: string;
47
- method: string;
48
- params: string;
49
- body: string;
50
- response: string;
51
- dependencies: EndpointDependency[];
52
- rules: string[];
53
- metadata: string;
54
- };
55
-
56
- export type ResolvedService = {
57
- concept: string;
58
- serviceName: string;
59
- builtInId: string;
60
- description: string;
61
- rules: string[];
62
- metadata: string;
63
- };
64
-
65
- export type ResolvedContext = {
66
- readEndpoints: ResolvedEndpoint[];
67
- writeEndpoints: ResolvedEndpoint[];
68
- services: ResolvedService[];
69
- };
70
-
71
- // ---------------------------------------------------------------------------
72
- // Internal row types
73
- // ---------------------------------------------------------------------------
74
-
75
- type EndpointRow = {
76
- concept: string;
77
- relationType: "QUERIED_VIA" | "MUTATED_VIA";
78
- name: string;
79
- path: string;
80
- method: string;
81
- params: string | null;
82
- body: string | null;
83
- response: string | null;
84
- metadata: string | null;
85
- dependencies: (EndpointDependency | { depPath: null })[];
86
- rules: (string | null)[];
87
- };
88
-
89
- type ServiceRow = {
90
- concept: string;
91
- serviceName: string;
92
- builtInId: string;
93
- description: string | null;
94
- metadata: string | null;
95
- rules: (string | null)[];
96
- };
97
-
98
- // ---------------------------------------------------------------------------
99
- // Public API
100
- // ---------------------------------------------------------------------------
101
-
102
- export async function resolveFromGraph(prompt: string, config: ResolverConfig) {
103
- const empty: ResolvedContext = {
104
- readEndpoints: [],
105
- writeEndpoints: [],
106
- services: [],
107
- };
108
-
109
- try {
110
- const { embedding } = await embed({
111
- model: config.embeddingModel,
112
- value: prompt,
113
- });
114
-
115
- const conceptsRaw = await config.neo4j.query(
116
- `CALL db.index.vector.queryNodes('concept_embeddings', 10, $embedding)
117
- YIELD node, score
118
- WHERE score > 0.3
119
- RETURN node.name AS name, node.description AS description, score
120
- ORDER BY score DESC`,
121
- { embedding },
122
- );
123
-
124
- let concepts = JSON.parse(conceptsRaw) as (Pick<ConceptDef, "name" | "description"> & {
125
- score: number;
126
- })[];
127
-
128
- if (!Array.isArray(concepts) || concepts.length === 0) {
129
- return empty;
130
- }
131
-
132
- if (config.reranker) {
133
- concepts = await computeRelevanceScores(prompt, concepts, config.reranker);
134
- } else {
135
- concepts = concepts.slice(0, 3);
136
- }
137
-
138
- const conceptNames = concepts.map((c) => c.name);
139
-
140
- const endpointsRaw = await config.neo4j.query(
141
- `UNWIND $names AS conceptName
142
- MATCH (c:Concept {name: conceptName})-[:SPECIALIZES*0..3]->(ancestor:Concept)
143
- WITH conceptName, collect(DISTINCT ancestor) AS family
144
- UNWIND family AS related
145
- MATCH (related)-[rel:QUERIED_VIA|MUTATED_VIA]->(e:Endpoint)
146
- WITH conceptName, related, rel, e
147
- OPTIONAL MATCH (e)-[d:DEPENDS_ON]->(dep:Endpoint)
148
- WITH conceptName, related, rel, e,
149
- collect(DISTINCT {
150
- depPath: dep.path,
151
- depMethod: dep.method,
152
- paramName: d.paramName,
153
- fromField: d.fromField
154
- }) AS dependencies
155
- OPTIONAL MATCH (rEndpoint:Rule)-[:GOVERNS]->(e)
156
- OPTIONAL MATCH (rConcept:Rule)-[:GOVERNS]->(related)
157
- RETURN conceptName AS concept,
158
- type(rel) AS relationType,
159
- e.name AS name,
160
- e.path AS path,
161
- e.method AS method,
162
- e.params AS params,
163
- e.body AS body,
164
- e.response AS response,
165
- e.metadata AS metadata,
166
- dependencies,
167
- collect(DISTINCT rEndpoint.description) +
168
- collect(DISTINCT rConcept.description) AS rules`,
169
- { names: conceptNames },
170
- );
171
-
172
- const endpointRows = asArray<EndpointRow>(JSON.parse(endpointsRaw));
173
- const allEndpoints = dedupeEndpoints(
174
- endpointRows.map(function (row) {
175
- return {
176
- concept: row.concept,
177
- relation:
178
- row.relationType === "MUTATED_VIA" ? ("write" as const) : ("read" as const),
179
- name: row.name,
180
- path: row.path,
181
- method: row.method,
182
- params: row.params ?? "[]",
183
- body: row.body ?? "[]",
184
- response: row.response ?? "[]",
185
- metadata: row.metadata ?? "{}",
186
- dependencies: row.dependencies.filter(
187
- (dep): dep is EndpointDependency => dep.depPath != null,
188
- ),
189
- rules: row.rules.filter((rule): rule is string => Boolean(rule)),
190
- };
191
- }),
192
- );
193
-
194
- const servicesRaw = await config.neo4j.query(
195
- `UNWIND $names AS conceptName
196
- MATCH (c:Concept {name: conceptName})-[:SPECIALIZES*0..3]->(ancestor:Concept)
197
- WITH conceptName, collect(DISTINCT ancestor) AS family
198
- UNWIND family AS related
199
- MATCH (related)-[:REQUESTED_VIA]->(svc:Service)
200
- WITH conceptName, related, svc
201
- OPTIONAL MATCH (rService:Rule)-[:GOVERNS]->(svc)
202
- OPTIONAL MATCH (rConcept:Rule)-[:GOVERNS]->(related)
203
- RETURN conceptName AS concept,
204
- svc.name AS serviceName,
205
- svc.builtInId AS builtInId,
206
- svc.description AS description,
207
- svc.metadata AS metadata,
208
- collect(DISTINCT rService.description) +
209
- collect(DISTINCT rConcept.description) AS rules`,
210
- { names: conceptNames },
211
- );
212
-
213
- const services = dedupeServices(
214
- asArray<ServiceRow>(JSON.parse(servicesRaw)).map(function (row) {
215
- return {
216
- concept: row.concept,
217
- serviceName: row.serviceName,
218
- builtInId: row.builtInId,
219
- description: row.description ?? "",
220
- metadata: row.metadata ?? "{}",
221
- rules: row.rules.filter((rule): rule is string => Boolean(rule)),
222
- };
223
- }),
224
- );
225
-
226
- // Service-to-Servicing bridge: if any services were resolved, pull in
227
- // the generic Servicing endpoints
228
- let servicingEndpoints: ResolvedEndpoint[] = [];
229
- if (services.length > 0) {
230
- const servicingRaw = await config.neo4j.query(
231
- `MATCH (c:Concept {name: 'ServiceRequest'})-[rel:QUERIED_VIA|MUTATED_VIA]->(e:Endpoint)
232
- OPTIONAL MATCH (e)-[d:DEPENDS_ON]->(dep:Endpoint)
233
- WITH c, rel, e,
234
- collect(DISTINCT {
235
- depPath: dep.path,
236
- depMethod: dep.method,
237
- paramName: d.paramName,
238
- fromField: d.fromField
239
- }) AS dependencies
240
- OPTIONAL MATCH (rEndpoint:Rule)-[:GOVERNS]->(e)
241
- OPTIONAL MATCH (rConcept:Rule)-[:GOVERNS]->(c)
242
- RETURN 'ServiceRequest' AS concept,
243
- type(rel) AS relationType,
244
- e.name AS name,
245
- e.path AS path,
246
- e.method AS method,
247
- e.params AS params,
248
- e.body AS body,
249
- e.response AS response,
250
- e.metadata AS metadata,
251
- dependencies,
252
- collect(DISTINCT rEndpoint.description) +
253
- collect(DISTINCT rConcept.description) AS rules`,
254
- {},
255
- );
256
-
257
- servicingEndpoints = dedupeEndpoints(
258
- asArray<EndpointRow>(JSON.parse(servicingRaw)).map(function (row) {
259
- return {
260
- concept: row.concept,
261
- relation:
262
- row.relationType === "MUTATED_VIA"
263
- ? ("write" as const)
264
- : ("read" as const),
265
- name: row.name,
266
- path: row.path,
267
- method: row.method,
268
- params: row.params ?? "[]",
269
- body: row.body ?? "[]",
270
- response: row.response ?? "[]",
271
- metadata: row.metadata ?? "{}",
272
- dependencies: row.dependencies.filter(
273
- (dep): dep is EndpointDependency => dep.depPath != null,
274
- ),
275
- rules: row.rules.filter((rule): rule is string => Boolean(rule)),
276
- };
277
- }),
278
- );
279
- }
280
-
281
- const combinedEndpoints = dedupeEndpoints([...allEndpoints, ...servicingEndpoints]);
282
-
283
- return {
284
- readEndpoints: combinedEndpoints.filter((ep) => ep.relation === "read"),
285
- writeEndpoints: combinedEndpoints.filter((ep) => ep.relation === "write"),
286
- services,
287
- } satisfies ResolvedContext;
288
- } catch (error) {
289
- console.error("Graph resolution failed:", error);
290
- return empty;
291
- }
292
- }
293
-
294
- // ---------------------------------------------------------------------------
295
- // Internal helpers
296
- // ---------------------------------------------------------------------------
297
-
298
- function asArray<T>(value: unknown) {
299
- return Array.isArray(value) ? (value as T[]) : ([] as T[]);
300
- }
301
-
302
- function dedupeEndpoints(endpoints: ResolvedEndpoint[]) {
303
- const seen = new Set<string>();
304
- const deduped: ResolvedEndpoint[] = [];
305
-
306
- for (const ep of endpoints) {
307
- const key = `${ep.relation}|${ep.concept}|${ep.method}|${ep.path}`;
308
- if (seen.has(key)) continue;
309
- seen.add(key);
310
- deduped.push(ep);
311
- }
312
-
313
- return deduped;
314
- }
315
-
316
- function dedupeServices(services: ResolvedService[]) {
317
- const seen = new Set<string>();
318
- const deduped: ResolvedService[] = [];
319
-
320
- for (const svc of services) {
321
- const key = `${svc.concept}|${svc.builtInId}`;
322
- if (seen.has(key)) continue;
323
- seen.add(key);
324
- deduped.push(svc);
325
- }
326
-
327
- return deduped;
328
- }
329
-
330
- async function computeRelevanceScores(
331
- prompt: string,
332
- concepts: (Pick<ConceptDef, "name" | "description"> & { score: number })[],
333
- reranker: RerankerConfig,
334
- ) {
335
- const response = await fetch(reranker.url, {
336
- method: "POST",
337
- headers: {
338
- Authorization: `Bearer ${reranker.apiKey}`,
339
- "Content-Type": "application/json",
340
- },
341
- body: JSON.stringify({
342
- queries: [prompt],
343
- documents: concepts.map((x) => x.description),
344
- }),
345
- });
346
-
347
- const data = (await response.json()) as { scores: number[] };
348
- const scores = data.scores;
349
-
350
- const scoredConcepts = concepts.map((c, idx) => ({
351
- ...c,
352
- score: scores[idx]!,
353
- }));
354
-
355
- scoredConcepts.sort((a, b) => b.score - a.score);
356
- return scoredConcepts.slice(0, 3);
357
- }
package/src/graph/seed.ts DELETED
@@ -1,162 +0,0 @@
1
- /**
2
- * Seed orchestrator for the knowledge graph.
3
- *
4
- * Validates domains, generates Cypher, executes against Neo4j,
5
- * and generates concept embeddings. All configuration is passed
6
- * explicitly — no environment variables or Convex dependencies.
7
- */
8
-
9
- import type { EmbeddingModel } from "ai";
10
- import { embed } from "ai";
11
- import type { Neo4jClient, Neo4jConfig } from "./neo4j.ts";
12
- import { createNeo4jClient } from "./neo4j.ts";
13
- import { expandDomains } from "./expand-domains.ts";
14
- import { generateCypher, type GeneratedCypher } from "./generate-cypher.ts";
15
- import type { DomainDef } from "./types.ts";
16
- import { validateDomain } from "./validate.ts";
17
-
18
- // ---------------------------------------------------------------------------
19
- // Config
20
- // ---------------------------------------------------------------------------
21
-
22
- export type EmbeddingConfig = {
23
- model: EmbeddingModel;
24
- dimension: number;
25
- };
26
-
27
- export type SeedGraphConfig = {
28
- neo4j: Neo4jConfig;
29
- embedding: EmbeddingConfig;
30
- };
31
-
32
- // ---------------------------------------------------------------------------
33
- // Internal helpers
34
- // ---------------------------------------------------------------------------
35
-
36
- async function runStatements(client: Neo4jClient, label: string, statements: string[]) {
37
- console.log(`\n--- ${label} (${statements.length} statements) ---`);
38
-
39
- let success = 0;
40
- let failed = 0;
41
-
42
- for (const stmt of statements) {
43
- try {
44
- const result = await client.query(stmt);
45
- const parsed = JSON.parse(result);
46
- if (parsed.error) {
47
- console.error(` ✗ Failed: ${parsed.message}`);
48
- console.error(` Statement: ${stmt.substring(0, 120)}...`);
49
- failed++;
50
- } else {
51
- success++;
52
- }
53
- } catch (e) {
54
- console.error(` ✗ Exception: ${e}`);
55
- console.error(` Statement: ${stmt.substring(0, 120)}...`);
56
- failed++;
57
- }
58
- }
59
-
60
- console.log(` ✓ ${success} succeeded, ${failed} failed`);
61
- }
62
-
63
- async function generateEmbeddings(client: Neo4jClient, embeddingConfig: EmbeddingConfig) {
64
- console.log("Seeding concept embeddings...");
65
-
66
- // 1. Create the vector index (idempotent)
67
- await client.query(
68
- `CREATE VECTOR INDEX concept_embeddings IF NOT EXISTS
69
- FOR (c:Concept) ON (c.embedding)
70
- OPTIONS {indexConfig: {
71
- \`vector.dimensions\`: ${embeddingConfig.dimension},
72
- \`vector.similarity_function\`: 'cosine'
73
- }}`,
74
- );
75
- console.log("Vector index created (or already exists)");
76
-
77
- // 2. Fetch all concepts
78
- const conceptsRaw = await client.query(
79
- "MATCH (c:Concept) RETURN c.name AS name, c.description AS description",
80
- );
81
- const concepts: { name: string; description: string }[] = JSON.parse(conceptsRaw);
82
-
83
- if (!Array.isArray(concepts) || concepts.length === 0) {
84
- console.log("No concepts found in Neo4j");
85
- return;
86
- }
87
-
88
- console.log(`Found ${concepts.length} concepts, generating embeddings...`);
89
-
90
- // 3. Generate and store embeddings for each concept
91
- const embeddingResults = await Promise.all(
92
- concepts.map(async (concept) => ({
93
- concept,
94
- embedding: (
95
- await embed({
96
- model: embeddingConfig.model,
97
- value: concept.description,
98
- })
99
- ).embedding,
100
- })),
101
- );
102
-
103
- // 4. Store the embeddings in the graph
104
- await Promise.all(
105
- embeddingResults.map(
106
- async (result) =>
107
- await client.query(
108
- `MATCH (c:Concept {name: $name})
109
- SET c.embedding = $embedding`,
110
- {
111
- name: result.concept.name,
112
- embedding: result.embedding,
113
- },
114
- ),
115
- ),
116
- );
117
-
118
- console.log("Embedding seeding complete!");
119
- }
120
-
121
- // ---------------------------------------------------------------------------
122
- // Public API
123
- // ---------------------------------------------------------------------------
124
-
125
- export async function seedGraph(config: SeedGraphConfig, domains: Record<string, DomainDef>) {
126
- const expandedDomains = expandDomains(domains);
127
-
128
- // Validate all domains
129
- const validationErrors = Object.entries(expandedDomains).flatMap(([name, domain]) =>
130
- validateDomain(domain).map((err) => `[${name}] ${err}`),
131
- );
132
-
133
- if (validationErrors.length) {
134
- validationErrors.forEach((err) => console.error(err));
135
- throw new Error(`Validation failed with ${validationErrors.length} error(s)`);
136
- }
137
-
138
- const client = createNeo4jClient(config.neo4j, config.embedding.model);
139
- const targets = Object.entries(expandedDomains);
140
-
141
- console.log(`Seeding ${targets.length} domain(s)...`);
142
-
143
- // Generate Cypher for all target domains
144
- const allCypher: [string, GeneratedCypher][] = targets.map(([name, data]) => [
145
- name,
146
- generateCypher(data),
147
- ]);
148
-
149
- // Phase 1: Create all nodes across every domain first.
150
- // This ensures cross-domain references resolve correctly.
151
- const allNodes = allCypher.flatMap(([, c]) => c.nodes);
152
- await runStatements(client, "Phase 1: Nodes", allNodes);
153
-
154
- // Phase 2: Create all edges now that every node exists.
155
- const allEdges = allCypher.flatMap(([, c]) => c.edges);
156
- await runStatements(client, "Phase 2: Edges", allEdges);
157
-
158
- console.log("\nGraph seeding complete!");
159
-
160
- // Phase 3: Generate embeddings for concepts
161
- await generateEmbeddings(client, config.embedding);
162
- }
@@ -1,78 +0,0 @@
1
- import type { DomainDef } from "./types.ts";
2
-
3
- export function validateDomain(data: DomainDef) {
4
- const errors: string[] = [];
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}"`);
10
- }
11
- seenConceptNames.add(c.name);
12
- }
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}"`);
18
- }
19
- seenEndpointNames.add(e.name);
20
- }
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}"`);
26
- }
27
- seenServiceNames.add(s.name);
28
- }
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}"`);
34
- }
35
- seenRuleNames.add(r.name);
36
- }
37
-
38
- // Cross-domain references are allowed (concepts/endpoints/services may
39
- // be defined in another domain), so we do not enforce local membership.
40
-
41
- const referencedConcepts = new Set<string>();
42
- const referencedRules = new Set<string>();
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
- }
50
-
51
- for (const svc of data.services ?? []) {
52
- referencedConcepts.add(svc.belongsTo.name);
53
- svc.governedBy?.forEach((r) => referencedRules.add(r.name));
54
- }
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);
60
- }
61
- }
62
-
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
- }
67
- }
68
-
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
- }
75
- }
76
-
77
- return errors;
78
- }
@@ -1,86 +0,0 @@
1
- import { Hono } from "hono";
2
- import { zValidator } from "@hono/zod-validator";
3
- import z from "zod";
4
- import { HTTPException } from "hono/http-exception";
5
- import type { CortexAppEnv } from "../types.ts";
6
- import { requireAuth } from "../auth/middleware.ts";
7
- import { stream } from "../ai/index.ts";
8
- import { subscribe, abortStream } from "../ai/active-streams.ts";
9
- import { notify } from "../ws/index.ts";
10
- import { toThreadSummary } from "../types.ts";
11
-
12
- export function createChatRoutes() {
13
- const app = new Hono<CortexAppEnv>();
14
-
15
- app.post(
16
- "/chat",
17
- requireAuth,
18
- zValidator(
19
- "json",
20
- z.object({
21
- id: z.string(),
22
- messages: z.array(z.unknown()),
23
- }),
24
- ),
25
- async function (c) {
26
- const config = c.get("agentConfig");
27
- const agentId = c.get("agentId");
28
- const { id: userId, token } = c.get("user");
29
- const { messages, id: threadId } = c.req.valid("json");
30
- const requestContext = config.resolveRequestContext
31
- ? await config.resolveRequestContext(c.req.raw)
32
- : {};
33
-
34
- const thread = await config.db.threads.getById(userId, threadId);
35
-
36
- if (!thread || thread.agentId !== agentId) {
37
- throw new HTTPException(404, { message: "Not found" });
38
- }
39
-
40
- return stream(messages, thread, userId, token, requestContext, config);
41
- },
42
- );
43
-
44
- app.get("/chat/:chatId/stream", requireAuth, async function (c) {
45
- const config = c.get("agentConfig");
46
- const agentId = c.get("agentId");
47
- const chatId = c.req.param("chatId");
48
- const thread = await config.db.threads.getById(c.get("user").id, chatId);
49
- if (!thread || thread.agentId !== agentId) {
50
- throw new HTTPException(404, { message: "Not found" });
51
- }
52
-
53
- const subscriberStream = subscribe(chatId);
54
- if (!subscriberStream) {
55
- return c.body(null, 204);
56
- }
57
-
58
- return new Response(subscriberStream.pipeThrough(new TextEncoderStream()), {
59
- headers: {
60
- "Content-Type": "text/event-stream",
61
- "Cache-Control": "no-cache",
62
- Connection: "keep-alive",
63
- },
64
- });
65
- });
66
-
67
- app.post("/chat/:chatId/abort", requireAuth, async function (c) {
68
- const config = c.get("agentConfig");
69
- const agentId = c.get("agentId");
70
- const userId = c.get("user").id;
71
- const chatId = c.req.param("chatId");
72
- const thread = await config.db.threads.getById(userId, chatId);
73
- if (!thread || thread.agentId !== agentId) {
74
- throw new HTTPException(404, { message: "Not found" });
75
- }
76
-
77
- abortStream(chatId);
78
- notify(userId, agentId, {
79
- type: "thread:run-finished",
80
- payload: { thread: toThreadSummary(thread, false) },
81
- });
82
- return c.body(null, 204);
83
- });
84
-
85
- return app;
86
- }