@m6d/cortex-server 1.6.0 → 1.8.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 (254) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/src/{adapters → lib/adapters}/database.d.ts +16 -1
  3. package/dist/src/lib/ai/context/compressor.d.ts +18 -0
  4. package/dist/src/{ai → lib/ai}/context/index.d.ts +2 -1
  5. package/dist/src/lib/ai/context/intra-turn-compressor.d.ts +24 -0
  6. package/dist/src/{ai → lib/ai}/context/types.d.ts +2 -0
  7. package/dist/src/lib/config.d.ts +153 -0
  8. package/dist/src/{db → lib/db}/schema.d.ts +104 -1
  9. package/dist/src/{index.d.ts → lib/index.d.ts} +2 -2
  10. package/dist/src/{types.d.ts → lib/types.d.ts} +15 -14
  11. package/index.ts +1 -1
  12. package/package.json +8 -3
  13. package/src/{adapters → lib/adapters}/database.ts +20 -1
  14. package/src/{adapters → lib/adapters}/minio.ts +2 -1
  15. package/src/{adapters → lib/adapters}/mssql.ts +38 -2
  16. package/src/lib/ai/context/compressor.ts +259 -0
  17. package/src/{ai → lib/ai}/context/index.ts +6 -1
  18. package/src/lib/ai/context/intra-turn-compressor.ts +264 -0
  19. package/src/{ai → lib/ai}/context/types.ts +4 -1
  20. package/src/{ai → lib/ai}/index.ts +114 -13
  21. package/src/lib/ai/prompt.ts +354 -0
  22. package/src/{ai → lib/ai}/tools/execute-code.tool.ts +5 -2
  23. package/src/{auth → lib/auth}/middleware.ts +7 -0
  24. package/src/lib/config.ts +181 -0
  25. package/src/{db/migrations/20260309012148_cloudy_maria_hill → lib/db/migrations/20260326231647_nice_speedball}/migration.sql +12 -0
  26. package/src/{db/migrations/20260309012148_cloudy_maria_hill → lib/db/migrations/20260326231647_nice_speedball}/snapshot.json +106 -1
  27. package/src/{db → lib/db}/schema.ts +15 -2
  28. package/src/{factory.ts → lib/factory.ts} +1 -0
  29. package/src/{index.ts → lib/index.ts} +3 -1
  30. package/src/{routes → lib/routes}/files.ts +9 -2
  31. package/src/{routes → lib/routes}/threads.ts +8 -1
  32. package/src/{types.ts → lib/types.ts} +16 -15
  33. package/src/sample/db/client.ts +9 -0
  34. package/src/sample/db/migrations/20260411023125_rich_purple_man/migration.sql +5 -0
  35. package/src/sample/db/migrations/20260411023125_rich_purple_man/snapshot.json +50 -0
  36. package/src/sample/db/schema.ts +10 -0
  37. package/src/sample/domains/account/concepts/session.concept.ts +17 -0
  38. package/src/sample/domains/account/endpoints/listCurrentSessions.endpoint.ts +31 -0
  39. package/src/sample/domains/account/endpoints/revokeAllOtherSessions.endpoint.ts +22 -0
  40. package/src/sample/domains/account/endpoints/revokeCurrentSession.endpoint.ts +33 -0
  41. package/src/sample/domains/account/index.ts +18 -0
  42. package/src/sample/domains/appraisals/concepts/appraisalCycle.concept.ts +15 -0
  43. package/src/sample/domains/appraisals/concepts/appraisalRecord.concept.ts +20 -0
  44. package/src/sample/domains/appraisals/concepts/appraisalRecordCompetency.concept.ts +10 -0
  45. package/src/sample/domains/appraisals/concepts/appraisalRecordFunctionalRequirement.concept.ts +10 -0
  46. package/src/sample/domains/appraisals/concepts/appraisalRecordGoal.concept.ts +17 -0
  47. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordById.endpoint.ts +321 -0
  48. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordCompetencies.endpoint.ts +120 -0
  49. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordFunctionalRequirements.endpoint.ts +126 -0
  50. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordGoals.endpoint.ts +104 -0
  51. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordScore.endpoint.ts +58 -0
  52. package/src/sample/domains/appraisals/endpoints/listAppraisalCyclesSimple.endpoint.ts +52 -0
  53. package/src/sample/domains/appraisals/endpoints/listAppraisalRecords.endpoint.ts +245 -0
  54. package/src/sample/domains/appraisals/endpoints/listAppraisalTemplatesSimple.endpoint.ts +52 -0
  55. package/src/sample/domains/appraisals/index.ts +40 -0
  56. package/src/sample/domains/appraisals/rules/appraisalRecordScoping.rule.ts +7 -0
  57. package/src/sample/domains/attendance/concepts/attendancePermission.concept.ts +17 -0
  58. package/src/sample/domains/attendance/concepts/attendanceTransaction.concept.ts +17 -0
  59. package/src/sample/domains/attendance/concepts/publicHoliday.concept.ts +10 -0
  60. package/src/sample/domains/attendance/concepts/punch.concept.ts +19 -0
  61. package/src/sample/domains/attendance/endpoints/getAttendanceTransactionById.endpoint.ts +444 -0
  62. package/src/sample/domains/attendance/endpoints/getCurrentAttendanceProfile.endpoint.ts +886 -0
  63. package/src/sample/domains/attendance/endpoints/getCurrentAttendanceStatistics.endpoint.ts +49 -0
  64. package/src/sample/domains/attendance/endpoints/listAttendancePermissions.endpoint.ts +176 -0
  65. package/src/sample/domains/attendance/endpoints/listAttendanceTransactionStates.endpoint.ts +26 -0
  66. package/src/sample/domains/attendance/endpoints/listAttendanceTransactions.endpoint.ts +373 -0
  67. package/src/sample/domains/attendance/endpoints/listPublicHolidaysSimple.endpoint.ts +52 -0
  68. package/src/sample/domains/attendance/endpoints/listPunchTypes.endpoint.ts +25 -0
  69. package/src/sample/domains/attendance/endpoints/listPunches.endpoint.ts +178 -0
  70. package/src/sample/domains/attendance/index.ts +41 -0
  71. package/src/sample/domains/attendance/services/attendancePermissions.service.ts +15 -0
  72. package/src/sample/domains/employees/concepts/employee.concept.ts +20 -0
  73. package/src/sample/domains/employees/concepts/employeeBankAccount.concept.ts +19 -0
  74. package/src/sample/domains/employees/concepts/employeeChild.concept.ts +19 -0
  75. package/src/sample/domains/employees/concepts/employeeDocument.concept.ts +21 -0
  76. package/src/sample/domains/employees/concepts/employeeEvent.concept.ts +21 -0
  77. package/src/sample/domains/employees/concepts/employeeExperience.concept.ts +18 -0
  78. package/src/sample/domains/employees/concepts/employeeJobLevelChange.concept.ts +21 -0
  79. package/src/sample/domains/employees/concepts/employeeMedal.concept.ts +20 -0
  80. package/src/sample/domains/employees/concepts/employeeQualification.concept.ts +20 -0
  81. package/src/sample/domains/employees/concepts/employeeSpouse.concept.ts +21 -0
  82. package/src/sample/domains/employees/concepts/employeeViolation.concept.ts +19 -0
  83. package/src/sample/domains/employees/endpoints/getCurrentEmployee.endpoint.ts +1651 -0
  84. package/src/sample/domains/employees/endpoints/getCurrentIdentity.endpoint.ts +197 -0
  85. package/src/sample/domains/employees/endpoints/getCurrentManagers.endpoint.ts +142 -0
  86. package/src/sample/domains/employees/endpoints/getEmployeeById.endpoint.ts +1659 -0
  87. package/src/sample/domains/employees/endpoints/getEmployeeStatistics.endpoint.ts +43 -0
  88. package/src/sample/domains/employees/endpoints/listBankAccounts.endpoint.ts +223 -0
  89. package/src/sample/domains/employees/endpoints/listChildren.endpoint.ts +211 -0
  90. package/src/sample/domains/employees/endpoints/listDocuments.endpoint.ts +222 -0
  91. package/src/sample/domains/employees/endpoints/listEmployeesSimple.endpoint.ts +156 -0
  92. package/src/sample/domains/employees/endpoints/listEvents.endpoint.ts +120 -0
  93. package/src/sample/domains/employees/endpoints/listExperiences.endpoint.ts +188 -0
  94. package/src/sample/domains/employees/endpoints/listJobLevelChanges.endpoint.ts +240 -0
  95. package/src/sample/domains/employees/endpoints/listMedals.endpoint.ts +266 -0
  96. package/src/sample/domains/employees/endpoints/listQualifications.endpoint.ts +265 -0
  97. package/src/sample/domains/employees/endpoints/listSpouses.endpoint.ts +278 -0
  98. package/src/sample/domains/employees/endpoints/listViolations.endpoint.ts +245 -0
  99. package/src/sample/domains/employees/index.ts +75 -0
  100. package/src/sample/domains/employees/services/employees.service.ts +181 -0
  101. package/src/sample/domains/index.ts +10 -0
  102. package/src/sample/domains/leaves/concepts/leave.concept.ts +21 -0
  103. package/src/sample/domains/leaves/concepts/leaveBalance.concept.ts +17 -0
  104. package/src/sample/domains/leaves/concepts/leaveBalanceTransaction.concept.ts +16 -0
  105. package/src/sample/domains/leaves/concepts/leaveType.concept.ts +13 -0
  106. package/src/sample/domains/leaves/concepts/leaveTypes.concept.ts +205 -0
  107. package/src/sample/domains/leaves/endpoints/getBalance.endpoint.ts +53 -0
  108. package/src/sample/domains/leaves/endpoints/getLeaveById.endpoint.ts +53 -0
  109. package/src/sample/domains/leaves/endpoints/listBalanceTransactions.endpoint.ts +196 -0
  110. package/src/sample/domains/leaves/endpoints/listBalanceTypes.endpoint.ts +26 -0
  111. package/src/sample/domains/leaves/endpoints/listBalances.endpoint.ts +201 -0
  112. package/src/sample/domains/leaves/endpoints/listLeaveTypes.endpoint.ts +28 -0
  113. package/src/sample/domains/leaves/endpoints/listLeaves.endpoint.ts +234 -0
  114. package/src/sample/domains/leaves/index.ts +38 -0
  115. package/src/sample/domains/leaves/services/leaveRequests.service.ts +232 -0
  116. package/src/sample/domains/notifications/concepts/notification.concept.ts +19 -0
  117. package/src/sample/domains/notifications/endpoints/countMyNotifications.endpoint.ts +21 -0
  118. package/src/sample/domains/notifications/endpoints/getMyNotification.endpoint.ts +57 -0
  119. package/src/sample/domains/notifications/endpoints/listMyNotifications.endpoint.ts +59 -0
  120. package/src/sample/domains/notifications/endpoints/markMyNotificationAsRead.endpoint.ts +32 -0
  121. package/src/sample/domains/notifications/index.ts +20 -0
  122. package/src/sample/domains/payroll/concepts/payslip.concept.ts +18 -0
  123. package/src/sample/domains/payroll/concepts/salaryCertificate.concept.ts +17 -0
  124. package/src/sample/domains/payroll/concepts/salaryCertificateRequestingEntity.concept.ts +8 -0
  125. package/src/sample/domains/payroll/endpoints/getPayslipById.endpoint.ts +238 -0
  126. package/src/sample/domains/payroll/endpoints/listPayslips.endpoint.ts +216 -0
  127. package/src/sample/domains/payroll/endpoints/listSalaryCertificateLanguages.endpoint.ts +25 -0
  128. package/src/sample/domains/payroll/endpoints/listSalaryCertificateRequestingEntities.endpoint.ts +62 -0
  129. package/src/sample/domains/payroll/endpoints/listSalaryCertificateTypes.endpoint.ts +24 -0
  130. package/src/sample/domains/payroll/endpoints/listSalaryCertificates.endpoint.ts +122 -0
  131. package/src/sample/domains/payroll/endpoints/previewSalaryCertificate.endpoint.ts +45 -0
  132. package/src/sample/domains/payroll/index.ts +32 -0
  133. package/src/sample/domains/payroll/rules/payrollEmployeeContext.rule.ts +7 -0
  134. package/src/sample/domains/payroll/services/salaryCertificates.service.ts +15 -0
  135. package/src/sample/domains/servicing/concepts/service.concept.ts +6 -0
  136. package/src/sample/domains/servicing/concepts/serviceRequest.concept.ts +12 -0
  137. package/src/sample/domains/servicing/concepts/serviceRequestApproval.concept.ts +9 -0
  138. package/src/sample/domains/servicing/endpoints/cancelServiceRequest.endpoint.ts +21 -0
  139. package/src/sample/domains/servicing/endpoints/createServiceRequest.endpoint.ts +511 -0
  140. package/src/sample/domains/servicing/endpoints/getServiceRequest.endpoint.ts +352 -0
  141. package/src/sample/domains/servicing/endpoints/getServiceRequestFormItems.endpoint.ts +219 -0
  142. package/src/sample/domains/servicing/endpoints/getServiceRequestTransactionFormItems.endpoint.ts +216 -0
  143. package/src/sample/domains/servicing/endpoints/getServiceStates.endpoint.ts +47 -0
  144. package/src/sample/domains/servicing/endpoints/invokeServiceRequestAction.endpoint.ts +139 -0
  145. package/src/sample/domains/servicing/endpoints/listServiceRequests.endpoint.ts +292 -0
  146. package/src/sample/domains/servicing/endpoints/listServices.endpoint.ts +108 -0
  147. package/src/sample/domains/servicing/endpoints/updateServiceRequest.endpoint.ts +355 -0
  148. package/src/sample/domains/servicing/index.ts +44 -0
  149. package/src/sample/domains/servicing/rules/checkValidActions.rule.ts +7 -0
  150. package/src/sample/domains/servicing/rules/createServiceRequest.rule.ts +7 -0
  151. package/src/sample/domains/servicing/rules/filterServiceRequestsByState.rule.ts +7 -0
  152. package/src/sample/domains/servicing/rules/passingOptionFields.rule.ts +7 -0
  153. package/src/sample/domains/shared/rules/pagination.rule.ts +7 -0
  154. package/src/sample/domains/suggestions/concepts/suggestion.concept.ts +19 -0
  155. package/src/sample/domains/suggestions/concepts/suggestionCategory.concept.ts +7 -0
  156. package/src/sample/domains/suggestions/concepts/suggestionImpactLevel.concept.ts +7 -0
  157. package/src/sample/domains/suggestions/endpoints/createSuggestion.endpoint.ts +705 -0
  158. package/src/sample/domains/suggestions/endpoints/deleteSuggestion.endpoint.ts +32 -0
  159. package/src/sample/domains/suggestions/endpoints/getSuggestionById.endpoint.ts +635 -0
  160. package/src/sample/domains/suggestions/endpoints/listSuggestionCategories.endpoint.ts +62 -0
  161. package/src/sample/domains/suggestions/endpoints/listSuggestionImpactLevels.endpoint.ts +62 -0
  162. package/src/sample/domains/suggestions/endpoints/listSuggestionStates.endpoint.ts +24 -0
  163. package/src/sample/domains/suggestions/endpoints/listSuggestions.endpoint.ts +132 -0
  164. package/src/sample/domains/suggestions/endpoints/updateSuggestion.endpoint.ts +690 -0
  165. package/src/sample/domains/suggestions/endpoints/updateSuggestionState.endpoint.ts +39 -0
  166. package/src/sample/domains/suggestions/index.ts +34 -0
  167. package/src/sample/domains/suggestions/rules/suggestionScope.rule.ts +7 -0
  168. package/src/sample/domains/surveys/concepts/survey.concept.ts +10 -0
  169. package/src/sample/domains/surveys/concepts/surveyResponse.concept.ts +14 -0
  170. package/src/sample/domains/surveys/endpoints/getSurveyResponseById.endpoint.ts +131 -0
  171. package/src/sample/domains/surveys/endpoints/getTargetedSurvey.endpoint.ts +129 -0
  172. package/src/sample/domains/surveys/endpoints/listTargetedSurveys.endpoint.ts +160 -0
  173. package/src/sample/domains/surveys/endpoints/respondToSurvey.endpoint.ts +144 -0
  174. package/src/sample/domains/surveys/index.ts +21 -0
  175. package/src/sample/drizzle.config.ts +11 -0
  176. package/src/sample/index.bak.ts +341 -0
  177. package/src/sample/index.ts +318 -0
  178. package/src/sample/official-statements/defaults.ts +57 -0
  179. package/src/sample/official-statements/routes.ts +137 -0
  180. package/src/sample/official-statements/service.ts +148 -0
  181. package/src/sample/official-statements/types.ts +23 -0
  182. package/dist/src/ai/context/compressor.d.ts +0 -7
  183. package/dist/src/ai/tools/call-endpoint.tool.d.ts +0 -7
  184. package/dist/src/config.d.ts +0 -159
  185. package/src/ai/context/compressor.ts +0 -47
  186. package/src/ai/prompt.ts +0 -126
  187. package/src/ai/tools/call-endpoint.tool.ts +0 -89
  188. package/src/config.ts +0 -164
  189. package/src/db/migrations/20260315000000_add_context_meta/migration.sql +0 -1
  190. package/dist/src/{adapters → lib/adapters}/minio.d.ts +0 -0
  191. package/dist/src/{adapters → lib/adapters}/mssql.d.ts +0 -0
  192. package/dist/src/{adapters → lib/adapters}/storage.d.ts +0 -0
  193. package/dist/src/{ai → lib/ai}/active-streams.d.ts +0 -0
  194. package/dist/src/{ai → lib/ai}/context/builder.d.ts +0 -0
  195. package/dist/src/{ai → lib/ai}/context/summarizer.d.ts +0 -0
  196. package/dist/src/{ai → lib/ai}/context/token-estimator.d.ts +0 -0
  197. package/dist/src/{ai → lib/ai}/fetch.d.ts +0 -0
  198. package/dist/src/{ai → lib/ai}/helpers.d.ts +0 -0
  199. package/dist/src/{ai → lib/ai}/index.d.ts +0 -0
  200. package/dist/src/{ai → lib/ai}/interceptors/request-interceptor.d.ts +0 -0
  201. package/dist/src/{ai → lib/ai}/prompt.d.ts +0 -0
  202. package/dist/src/{ai → lib/ai}/tools/capture-files.tool.d.ts +0 -0
  203. package/dist/src/{ai → lib/ai}/tools/execute-code.tool.d.ts +0 -0
  204. package/dist/src/{ai → lib/ai}/tools/query-graph.tool.d.ts +0 -0
  205. package/dist/src/{auth → lib/auth}/middleware.d.ts +0 -0
  206. package/dist/src/{cli → lib/cli}/extract-endpoints.d.ts +0 -0
  207. package/dist/src/{db → lib/db}/migrate.d.ts +0 -0
  208. package/dist/src/{factory.d.ts → lib/factory.d.ts} +0 -0
  209. package/dist/src/{graph → lib/graph}/expand-domains.d.ts +0 -0
  210. package/dist/src/{graph → lib/graph}/generate-cypher.d.ts +0 -0
  211. package/dist/src/{graph → lib/graph}/helpers.d.ts +2 -2
  212. /package/dist/src/{graph → lib/graph}/index.d.ts +0 -0
  213. /package/dist/src/{graph → lib/graph}/neo4j.d.ts +0 -0
  214. /package/dist/src/{graph → lib/graph}/resolver.d.ts +0 -0
  215. /package/dist/src/{graph → lib/graph}/seed.d.ts +0 -0
  216. /package/dist/src/{graph → lib/graph}/types.d.ts +0 -0
  217. /package/dist/src/{graph → lib/graph}/validate.d.ts +0 -0
  218. /package/dist/src/{routes → lib/routes}/chat.d.ts +0 -0
  219. /package/dist/src/{routes → lib/routes}/files.d.ts +0 -0
  220. /package/dist/src/{routes → lib/routes}/index.d.ts +0 -0
  221. /package/dist/src/{routes → lib/routes}/threads.d.ts +0 -0
  222. /package/dist/src/{routes → lib/routes}/ws.d.ts +0 -0
  223. /package/dist/src/{ws → lib/ws}/connections.d.ts +0 -0
  224. /package/dist/src/{ws → lib/ws}/events.d.ts +0 -0
  225. /package/dist/src/{ws → lib/ws}/index.d.ts +0 -0
  226. /package/dist/src/{ws → lib/ws}/notify.d.ts +0 -0
  227. /package/src/{adapters → lib/adapters}/storage.ts +0 -0
  228. /package/src/{ai → lib/ai}/active-streams.ts +0 -0
  229. /package/src/{ai → lib/ai}/context/builder.ts +0 -0
  230. /package/src/{ai → lib/ai}/context/summarizer.ts +0 -0
  231. /package/src/{ai → lib/ai}/context/token-estimator.ts +0 -0
  232. /package/src/{ai → lib/ai}/fetch.ts +0 -0
  233. /package/src/{ai → lib/ai}/helpers.ts +0 -0
  234. /package/src/{ai → lib/ai}/interceptors/request-interceptor.ts +0 -0
  235. /package/src/{ai → lib/ai}/tools/capture-files.tool.ts +0 -0
  236. /package/src/{ai → lib/ai}/tools/query-graph.tool.ts +0 -0
  237. /package/src/{cli → lib/cli}/extract-endpoints.ts +0 -0
  238. /package/src/{db → lib/db}/migrate.ts +0 -0
  239. /package/src/{graph → lib/graph}/expand-domains.ts +0 -0
  240. /package/src/{graph → lib/graph}/generate-cypher.ts +0 -0
  241. /package/src/{graph → lib/graph}/helpers.ts +0 -0
  242. /package/src/{graph → lib/graph}/index.ts +0 -0
  243. /package/src/{graph → lib/graph}/neo4j.ts +0 -0
  244. /package/src/{graph → lib/graph}/resolver.ts +0 -0
  245. /package/src/{graph → lib/graph}/seed.ts +0 -0
  246. /package/src/{graph → lib/graph}/types.ts +0 -0
  247. /package/src/{graph → lib/graph}/validate.ts +0 -0
  248. /package/src/{routes → lib/routes}/chat.ts +0 -0
  249. /package/src/{routes → lib/routes}/index.ts +0 -0
  250. /package/src/{routes → lib/routes}/ws.ts +0 -0
  251. /package/src/{ws → lib/ws}/connections.ts +0 -0
  252. /package/src/{ws → lib/ws}/events.ts +0 -0
  253. /package/src/{ws → lib/ws}/index.ts +0 -0
  254. /package/src/{ws → lib/ws}/notify.ts +0 -0
@@ -7,14 +7,14 @@ import {
7
7
  safeValidateUIMessages,
8
8
  stepCountIs,
9
9
  streamText,
10
+ wrapLanguageModel,
10
11
  } from "ai";
11
12
  import { HTTPException } from "hono/http-exception";
12
13
  import type { ResolvedCortexAgentConfig } from "../config.ts";
13
- import type { MessageMetadata, Thread } from "../types.ts";
14
+ import type { MessageMetadata, Thread, TokenUsage } from "../types.ts";
14
15
  import { createModel, createEmbeddingModel } from "./helpers.ts";
15
16
  import { buildSystemPrompt, resolveSession } from "./prompt.ts";
16
17
  import { createQueryGraphTool } from "./tools/query-graph.tool.ts";
17
- import { createCallEndpointTool } from "./tools/call-endpoint.tool.ts";
18
18
  import { createExecuteCodeTool } from "./tools/execute-code.tool.ts";
19
19
  import { captureFilesTool } from "./tools/capture-files.tool.ts";
20
20
  import { createRequestInterceptor } from "./interceptors/request-interceptor.ts";
@@ -23,6 +23,11 @@ import { resolveFromGraph } from "../graph/resolver.ts";
23
23
  import { notify } from "../ws/index.ts";
24
24
  import { buildContextMessages } from "./context/builder.ts";
25
25
  import { optimizeThreadContext, estimateTokens, trimMessagesToFit } from "./context/index.ts";
26
+ import {
27
+ compressIntraTurnToolResults,
28
+ estimateToolResultTokens,
29
+ summarizeOldStepResults,
30
+ } from "./context/intra-turn-compressor.ts";
26
31
  import {
27
32
  registerStream,
28
33
  attachSseStream,
@@ -67,18 +72,20 @@ export async function stream(
67
72
 
68
73
  const model = createModel(config.model);
69
74
  const embeddingModel = createEmbeddingModel(config.embedding);
70
- const neo4j = createNeo4jClient(config.neo4j, embeddingModel);
75
+ const neo4j = config.neo4j ? createNeo4jClient(config.neo4j, embeddingModel) : undefined;
71
76
 
72
77
  // Run independent operations in parallel
73
78
  const [contextResult, resolved, session] = await Promise.all([
74
79
  // Branch A: Load messages + build token-aware context window
75
80
  buildContextMessages(userId, thread, config.db, config.context),
76
81
  // Branch B: Resolve graph context (400-2000ms, the bottleneck)
77
- resolveFromGraph(prompt, {
78
- neo4j,
79
- embeddingModel,
80
- reranker: config.reranker,
81
- }),
82
+ neo4j
83
+ ? resolveFromGraph(prompt, {
84
+ neo4j,
85
+ embeddingModel,
86
+ reranker: config.reranker,
87
+ })
88
+ : Promise.resolve(null),
82
89
  // Branch C: Resolve session data
83
90
  resolveSession(config, thread, token),
84
91
  ]);
@@ -88,9 +95,12 @@ export async function stream(
88
95
  // Build tools
89
96
  const builtInTools: ToolSet = {
90
97
  captureFiles: captureFilesTool,
91
- queryGraph: createQueryGraphTool(neo4j),
92
98
  };
93
99
 
100
+ if (neo4j) {
101
+ builtInTools["queryGraph"] = createQueryGraphTool(neo4j);
102
+ }
103
+
94
104
  if (config.backendFetch) {
95
105
  const backendFetchWithInterceptor = {
96
106
  ...config.backendFetch,
@@ -103,13 +113,17 @@ export async function stream(
103
113
  ),
104
114
  };
105
115
 
106
- builtInTools["callEndpoint"] = createCallEndpointTool(backendFetchWithInterceptor, token);
107
116
  builtInTools["executeCode"] = createExecuteCodeTool(backendFetchWithInterceptor, token);
108
117
  }
109
118
 
119
+ const resolvedUserTools =
120
+ typeof config.tools === "function"
121
+ ? config.tools({ thread, userId, token, session, requestContext })
122
+ : config.tools;
123
+
110
124
  const tools = {
111
125
  ...builtInTools,
112
- ...config.tools,
126
+ ...resolvedUserTools,
113
127
  } as ToolSet;
114
128
 
115
129
  const systemPrompt = await buildSystemPrompt(config, resolved, {
@@ -129,13 +143,82 @@ export async function stream(
129
143
 
130
144
  const recentMessages = await convertToModelMessages(trimmedMessages);
131
145
 
146
+ const { context: contextConfig } = config;
147
+
148
+ const capturedSteps: {
149
+ prompt: string;
150
+ output: string | null;
151
+ tokenUsage: TokenUsage | null;
152
+ }[] = [];
153
+
132
154
  const result = streamText({
133
- model,
155
+ model: wrapLanguageModel({
156
+ model,
157
+ middleware: {
158
+ specificationVersion: "v3",
159
+ transformParams: async ({ params }) => {
160
+ // Strategy 1: Compress older step tool results to one-liners
161
+ let optimizedPrompt = compressIntraTurnToolResults(
162
+ params.prompt,
163
+ contextConfig.toolResultMaxTokens,
164
+ );
165
+
166
+ // Strategy 5: If still over threshold, summarize with LLM
167
+ const summarizationModel = contextConfig.summarizationModel ?? config.model;
168
+ const toolTokens = estimateToolResultTokens(optimizedPrompt);
169
+
170
+ if (toolTokens > contextConfig.intraTurnSummarizationThresholdTokens) {
171
+ try {
172
+ optimizedPrompt = await summarizeOldStepResults(
173
+ optimizedPrompt,
174
+ contextConfig.intraTurnSummarizationThresholdTokens,
175
+ summarizationModel,
176
+ );
177
+ } catch (err) {
178
+ console.error("[cortex-server] Intra-turn summarization failed:", err);
179
+ }
180
+ }
181
+
182
+ return { ...params, prompt: optimizedPrompt };
183
+ },
184
+ },
185
+ }),
134
186
  system: systemPrompt,
135
187
  tools,
136
188
  messages: recentMessages,
137
189
  abortSignal: abortController.signal,
138
- stopWhen: stepCountIs(50),
190
+ stopWhen: stepCountIs(25),
191
+ onStepFinish: (step) => {
192
+ const usage = step.usage;
193
+ capturedSteps.push({
194
+ prompt: JSON.stringify(step.request.body),
195
+ output: JSON.stringify({
196
+ text: step.text,
197
+ content: step.content,
198
+ finishReason: step.finishReason,
199
+ response: {
200
+ id: step.response.id,
201
+ modelId: step.response.modelId,
202
+ messages: step.response.messages,
203
+ },
204
+ providerMetadata: step.providerMetadata,
205
+ }),
206
+ tokenUsage: {
207
+ input: {
208
+ noCache: usage.inputTokenDetails.noCacheTokens ?? 0,
209
+ cacheRead: usage.inputTokenDetails.cacheReadTokens ?? 0,
210
+ cacheWrite: usage.inputTokenDetails.cacheWriteTokens ?? 0,
211
+ total: usage.inputTokens ?? 0,
212
+ },
213
+ output: {
214
+ reasoning: usage.outputTokenDetails.reasoningTokens ?? 0,
215
+ text: usage.outputTokenDetails.textTokens ?? 0,
216
+ total: usage.outputTokens ?? 0,
217
+ },
218
+ total: usage.totalTokens ?? 0,
219
+ },
220
+ });
221
+ },
139
222
  });
140
223
 
141
224
  return result.toUIMessageStreamResponse<UIMessage<MessageMetadata>>({
@@ -193,6 +276,24 @@ export async function stream(
193
276
  }
194
277
 
195
278
  await config.db.messages.upsert(thread.id, finishedMessages);
279
+
280
+ // Persist captured LLM request/response data per step
281
+ if (capturedSteps.length > 0 && lastAssistantMessage) {
282
+ const llmRequestInserts = capturedSteps.map((step, index) => ({
283
+ messageId: lastAssistantMessage.id,
284
+ stepNumber: index,
285
+ prompt: step.prompt,
286
+ output: step.output,
287
+ tokenUsage: step.tokenUsage,
288
+ }));
289
+
290
+ try {
291
+ await config.db.llmRequests.insert(llmRequestInserts);
292
+ } catch (err) {
293
+ console.error("[cortex-server] Failed to persist AI requests:", err);
294
+ }
295
+ }
296
+
196
297
  config.onStreamFinish?.({ messages: finishedMessages, isAborted });
197
298
 
198
299
  // XXX: we need to notify the user so that the client can
@@ -0,0 +1,354 @@
1
+ import type { ResolvedContext, ResolvedEndpoint, ResolvedService } from "../graph/resolver.ts";
2
+ import type { PromptContext, ResolvedCortexAgentConfig } from "../config.ts";
3
+ import type { Thread } from "../types.ts";
4
+
5
+ /**
6
+ * Resolves session data for the thread, loading from the configured
7
+ * session loader if not already cached on the thread.
8
+ */
9
+ export async function resolveSession(
10
+ config: ResolvedCortexAgentConfig,
11
+ thread: Thread,
12
+ token: string,
13
+ ) {
14
+ let session = thread.session;
15
+
16
+ if (!session && config.loadSessionData) {
17
+ session = await config.loadSessionData(token);
18
+ // Persist to DB for future cache hits
19
+ await config.db.threads.updateSession(thread.id, session);
20
+ thread.session = session;
21
+ }
22
+
23
+ return session;
24
+ }
25
+
26
+ export async function buildSystemPrompt(
27
+ config: ResolvedCortexAgentConfig,
28
+ resolved: ResolvedContext | null,
29
+ promptContext: PromptContext,
30
+ ) {
31
+ // Resolve the consumer's base system prompt
32
+ let basePrompt: string;
33
+ if (typeof config.systemPrompt === "function") {
34
+ basePrompt = await config.systemPrompt(promptContext);
35
+ } else {
36
+ basePrompt = config.systemPrompt;
37
+ }
38
+
39
+ const sections: string[] = [basePrompt];
40
+
41
+ // Pre-resolved endpoints from knowledge graph
42
+ if (resolved) {
43
+ sections.push(buildResolvedSection(resolved));
44
+ }
45
+
46
+ return sections.join("\n");
47
+ }
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Schema field type (parsed from endpoint JSON strings)
51
+ // ---------------------------------------------------------------------------
52
+
53
+ type SchemaField = {
54
+ name: string;
55
+ required?: boolean;
56
+ type?: string;
57
+ isArray?: boolean;
58
+ properties?: SchemaField[];
59
+ };
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // Layer 2: Schema compression
63
+ // ---------------------------------------------------------------------------
64
+
65
+ /** Compact param/body: `name?: type, nested?: {a: string, b: number}[]` */
66
+ function compactParamFields(fields: SchemaField[]): string {
67
+ return fields
68
+ .map((f) => {
69
+ const opt = f.required ? "" : "?";
70
+ const arr = f.isArray ? "[]" : "";
71
+ if (f.properties && f.properties.length > 0) {
72
+ return `${f.name}${opt}: {${compactParamFields(f.properties)}}${arr}`;
73
+ }
74
+ return `${f.name}${opt}: ${f.type ?? "unknown"}${arr}`;
75
+ })
76
+ .join(", ");
77
+ }
78
+
79
+ function compactParams(jsonStr: string) {
80
+ try {
81
+ const fields = JSON.parse(jsonStr) as SchemaField[];
82
+ if (!Array.isArray(fields) || fields.length === 0) return "(none)";
83
+ return compactParamFields(fields);
84
+ } catch {
85
+ return jsonStr;
86
+ }
87
+ }
88
+
89
+ /** Compact response: `items[].{id, employee.{id, name.{ar, en}}}, count` */
90
+ function compactResponseFields(fields: SchemaField[]): string {
91
+ return fields
92
+ .map((f) => {
93
+ const arr = f.isArray ? "[]" : "";
94
+ if (f.properties && f.properties.length > 0) {
95
+ return `${f.name}${arr}.{${compactResponseFields(f.properties)}}`;
96
+ }
97
+ return f.name;
98
+ })
99
+ .join(", ");
100
+ }
101
+
102
+ function compactResponse(jsonStr: string) {
103
+ try {
104
+ const fields = JSON.parse(jsonStr) as SchemaField[];
105
+ if (!Array.isArray(fields) || fields.length === 0) return "(none)";
106
+ return compactResponseFields(fields);
107
+ } catch {
108
+ return jsonStr;
109
+ }
110
+ }
111
+
112
+ // ---------------------------------------------------------------------------
113
+ // Layer 1: Group endpoints by method + path
114
+ // ---------------------------------------------------------------------------
115
+
116
+ type GroupedEndpoint = Omit<ResolvedEndpoint, "concept"> & {
117
+ concepts: string[];
118
+ };
119
+
120
+ function groupEndpointsBySignature(endpoints: ResolvedEndpoint[]) {
121
+ const groups = new Map<string, GroupedEndpoint>();
122
+
123
+ for (const ep of endpoints) {
124
+ const key = `${ep.method}|${ep.path}`;
125
+ const existing = groups.get(key);
126
+
127
+ if (existing) {
128
+ if (!existing.concepts.includes(ep.concept)) {
129
+ existing.concepts.push(ep.concept);
130
+ }
131
+ for (const rule of ep.rules) {
132
+ if (!existing.rules.includes(rule)) {
133
+ existing.rules.push(rule);
134
+ }
135
+ }
136
+ for (const dep of ep.dependencies) {
137
+ const isDuplicate = existing.dependencies.some(
138
+ (d) => d.depPath === dep.depPath && d.paramName === dep.paramName,
139
+ );
140
+ if (!isDuplicate) {
141
+ existing.dependencies.push(dep);
142
+ }
143
+ }
144
+ } else {
145
+ groups.set(key, {
146
+ concepts: [ep.concept],
147
+ relation: ep.relation,
148
+ name: ep.name,
149
+ path: ep.path,
150
+ method: ep.method,
151
+ params: ep.params,
152
+ body: ep.body,
153
+ response: ep.response,
154
+ dependencies: [...ep.dependencies],
155
+ rules: [...ep.rules],
156
+ metadata: ep.metadata,
157
+ });
158
+ }
159
+ }
160
+
161
+ return Array.from(groups.values());
162
+ }
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // Layer 3: Extract shared rules into a global section
166
+ // ---------------------------------------------------------------------------
167
+
168
+ function extractSharedRules(groups: GroupedEndpoint[], services: ResolvedService[]) {
169
+ const ruleCounts = new Map<string, number>();
170
+
171
+ for (const ep of groups) {
172
+ for (const rule of ep.rules) {
173
+ ruleCounts.set(rule, (ruleCounts.get(rule) ?? 0) + 1);
174
+ }
175
+ }
176
+ for (const svc of services) {
177
+ for (const rule of svc.rules) {
178
+ ruleCounts.set(rule, (ruleCounts.get(rule) ?? 0) + 1);
179
+ }
180
+ }
181
+
182
+ const sharedRules = [...ruleCounts.entries()]
183
+ .filter(([, count]) => count >= 2)
184
+ .map(([rule]) => rule);
185
+
186
+ const sharedSet = new Set(sharedRules);
187
+
188
+ return {
189
+ sharedRules,
190
+ groups: groups.map((ep) => ({
191
+ ...ep,
192
+ rules: ep.rules.filter((r) => !sharedSet.has(r)),
193
+ })),
194
+ services: services.map((svc) => ({
195
+ ...svc,
196
+ rules: svc.rules.filter((r) => !sharedSet.has(r)),
197
+ })),
198
+ };
199
+ }
200
+
201
+ // ---------------------------------------------------------------------------
202
+ // Layer 4: Deduplicate identical response schemas across endpoints
203
+ // ---------------------------------------------------------------------------
204
+
205
+ type ResponseShapeRefs = {
206
+ shapeDefs: { name: string; content: string }[];
207
+ refMap: Map<string, string>;
208
+ };
209
+
210
+ function buildResponseShapeRefs(groups: GroupedEndpoint[]): ResponseShapeRefs {
211
+ // Key on the raw JSON string (byte-identical for endpoints sharing the same
212
+ // Endpoint node in Neo4j). Fall back to the compact string for cases where
213
+ // different raw JSON produces the same compact shape.
214
+ const rawToEndpoints = new Map<string, string[]>();
215
+
216
+ for (const ep of groups) {
217
+ if (ep.response === "[]") continue;
218
+ const existing = rawToEndpoints.get(ep.response);
219
+ if (existing) {
220
+ existing.push(`${ep.method} ${ep.path}`);
221
+ } else {
222
+ rawToEndpoints.set(ep.response, [`${ep.method} ${ep.path}`]);
223
+ }
224
+ }
225
+
226
+ // Second pass: also group by compact output to catch different raw JSON
227
+ // that normalizes to the same shape
228
+ const compactToRaws = new Map<string, Set<string>>();
229
+ for (const raw of rawToEndpoints.keys()) {
230
+ const compact = compactResponse(raw);
231
+ const existing = compactToRaws.get(compact);
232
+ if (existing) {
233
+ existing.add(raw);
234
+ } else {
235
+ compactToRaws.set(compact, new Set([raw]));
236
+ }
237
+ }
238
+
239
+ // Merge: a shape is shared if its raw string appears on 2+ endpoints,
240
+ // OR if multiple raw strings compact to the same output
241
+ const shapeDefs: { name: string; content: string }[] = [];
242
+ const refMap = new Map<string, string>();
243
+ let idx = 1;
244
+
245
+ for (const [compact, raws] of compactToRaws) {
246
+ let totalEndpoints = 0;
247
+ for (const raw of raws) {
248
+ totalEndpoints += rawToEndpoints.get(raw)!.length;
249
+ }
250
+
251
+ if (totalEndpoints >= 2) {
252
+ const name = `$R${idx++}`;
253
+ shapeDefs.push({ name, content: compact });
254
+ refMap.set(compact, name);
255
+ }
256
+ }
257
+
258
+ return { shapeDefs, refMap };
259
+ }
260
+
261
+ // ---------------------------------------------------------------------------
262
+ // Pre-resolved section builder
263
+ // ---------------------------------------------------------------------------
264
+
265
+ function buildResolvedSection(resolved: ResolvedContext) {
266
+ const allEndpoints = [...resolved.readEndpoints, ...resolved.writeEndpoints];
267
+
268
+ if (allEndpoints.length === 0 && resolved.services.length === 0) {
269
+ return "\n## Pre-resolved API Endpoints\nNo matching endpoints found. Use queryGraph to search the knowledge graph manually.";
270
+ }
271
+
272
+ // Layer 1: deduplicate endpoints by method+path
273
+ const grouped = groupEndpointsBySignature(allEndpoints);
274
+
275
+ // Layer 3: hoist repeated rules to a shared section
276
+ const {
277
+ sharedRules,
278
+ groups: cleanedGroups,
279
+ services: cleanedServices,
280
+ } = extractSharedRules(grouped, resolved.services);
281
+
282
+ // Layer 4: deduplicate identical response schemas
283
+ const { shapeDefs, refMap } = buildResponseShapeRefs(cleanedGroups);
284
+
285
+ const parts: string[] = [
286
+ `
287
+ ## Pre-resolved API Endpoints
288
+ The following endpoints were automatically matched to the user's message.`,
289
+ ];
290
+
291
+ // Shared rules
292
+ if (sharedRules.length > 0) {
293
+ parts.push("\n### General Rules");
294
+ for (const rule of sharedRules) {
295
+ parts.push(`\n- ${rule}`);
296
+ }
297
+ }
298
+
299
+ // Response shape definitions
300
+ if (shapeDefs.length > 0) {
301
+ parts.push("\n### Response Shapes");
302
+ for (const def of shapeDefs) {
303
+ parts.push(`\n${def.name}: ${def.content}`);
304
+ }
305
+ }
306
+
307
+ // Endpoints (Layer 2: compact schemas)
308
+ for (const ep of cleanedGroups) {
309
+ const concepts = ep.concepts.join(", ");
310
+ const params = compactParams(ep.params);
311
+ const body = compactParams(ep.body);
312
+ const compactResp = compactResponse(ep.response);
313
+ const response = refMap.get(compactResp) ?? compactResp;
314
+
315
+ const rules = ep.rules.length > 0 ? `\n Rules: ${ep.rules.join("; ")}` : "";
316
+ const deps =
317
+ ep.dependencies.length > 0
318
+ ? "\n Dependencies:\n" +
319
+ ep.dependencies
320
+ .map(
321
+ (d) =>
322
+ ` - Call ${d.depMethod} ${d.depPath} first → use its "${d.fromField}" as "${d.paramName}"`,
323
+ )
324
+ .join("\n")
325
+ : "";
326
+ const meta = ep.metadata !== "{}" ? `\n Metadata: ${ep.metadata}` : "";
327
+
328
+ let block = `\n### ${ep.name} — ${ep.method} ${ep.path} (${ep.relation})`;
329
+ block += `\n- Concepts: ${concepts}`;
330
+ block += `\n- Params: ${params}`;
331
+ if (body !== "(none)") {
332
+ block += `\n- Body: ${body}`;
333
+ }
334
+ block += `\n- Response: ${response}`;
335
+ block += rules;
336
+ block += deps;
337
+ block += meta;
338
+
339
+ parts.push(block);
340
+ }
341
+
342
+ // Services
343
+ for (const svc of cleanedServices) {
344
+ const rules = svc.rules.length > 0 ? `\n Rules: ${svc.rules.join("; ")}` : "";
345
+ const meta = svc.metadata !== "{}" ? `\n- Metadata: ${svc.metadata}` : "";
346
+ parts.push(
347
+ `\n### ${svc.concept} via ${svc.serviceName} (service)` +
348
+ `\n- Built-in ID: ${svc.builtInId}` +
349
+ `\n- Description: ${svc.description || "N/A"}${rules}${meta}`,
350
+ );
351
+ }
352
+
353
+ return parts.join("");
354
+ }
@@ -131,8 +131,11 @@ export function createExecuteCodeTool(
131
131
  ) {
132
132
  return tool({
133
133
  title: "Executes JavaScript code",
134
- description:
135
- "Run a JavaScript script that calls APIs and returns only relevant data. The script has an `api` helper: api.get(path, params?), api.post(path, body?), api.put(path, body?), api.del(path). Each returns parsed JSON and throws on error. For parameters of type file, use `capturedFile#[uploadId]` as the value.",
134
+ description: `Run a JavaScript script that calls APIs and returns only relevant data.
135
+ The script has an \`api\` helper: api.get(path, params?), api.post(path, body?), api.put(path, body?), api.del(path).
136
+ Each returns parsed JSON and throws on error.
137
+ For parameters of type file, use \`capturedFile#[uploadId]\` as the value. If you try to upload files in any other way
138
+ you are going to get an error. All file uploads follow this rule without exception.`,
136
139
  inputSchema: z.object({
137
140
  code: z
138
141
  .string()
@@ -6,6 +6,13 @@ import type { AppEnv, AuthedAppEnv } from "../types";
6
6
  import type { CortexConfig } from "../config";
7
7
 
8
8
  export function createUserLoaderMiddleware(authConfig: CortexConfig["auth"]) {
9
+ if (!authConfig) {
10
+ return createMiddleware<AppEnv>(async (c, next) => {
11
+ c.set("user", { id: "00000000-0000-0000-0000-000000000000", token: "" });
12
+ await next();
13
+ });
14
+ }
15
+
9
16
  const jwks = createRemoteJWKSet(new URL(authConfig.jwksUri));
10
17
 
11
18
  return createMiddleware<AppEnv>(async (c, next) => {