@m6d/cortex-server 1.8.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 (352) 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/lib → contracts}/graph/helpers.ts +4 -10
  5. package/{src/lib → contracts}/graph/neo4j.ts +14 -27
  6. package/contracts/graph/schema.ts +65 -0
  7. package/{src/lib → 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/lib → contracts}/graph/helpers.d.ts +16 -14
  13. package/dist/{src/lib → contracts}/graph/neo4j.d.ts +5 -5
  14. package/dist/contracts/graph/schema.d.ts +62 -0
  15. package/dist/{src/lib → 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/src/lib/adapters/database/index.d.ts +61 -0
  20. package/dist/src/lib/adapters/database/message-content.d.ts +30 -0
  21. package/dist/src/lib/adapters/database/mssql/attachments.d.ts +100 -0
  22. package/dist/src/lib/adapters/database/mssql/client.d.ts +8 -0
  23. package/dist/src/lib/adapters/database/mssql/index.d.ts +194 -0
  24. package/dist/src/lib/adapters/database/mssql/llm-requests.d.ts +17 -0
  25. package/dist/src/lib/adapters/database/mssql/messages.d.ts +37 -0
  26. package/dist/src/lib/adapters/database/mssql/threads.d.ts +50 -0
  27. package/dist/src/lib/adapters/database/postgres/attachments.d.ts +113 -0
  28. package/dist/src/lib/adapters/database/postgres/client.d.ts +8 -0
  29. package/dist/src/lib/adapters/database/postgres/index.d.ts +207 -0
  30. package/dist/src/lib/adapters/database/postgres/llm-requests.d.ts +17 -0
  31. package/dist/src/lib/adapters/database/postgres/messages.d.ts +37 -0
  32. package/dist/src/lib/adapters/database/postgres/threads.d.ts +50 -0
  33. package/{src/lib/adapters/storage.ts → dist/src/lib/adapters/storage/index.d.ts} +1 -1
  34. package/dist/src/lib/adapters/{minio.d.ts → storage/minio.d.ts} +1 -1
  35. package/dist/src/lib/ai/active-runs.d.ts +63 -0
  36. package/dist/src/lib/ai/attachments.d.ts +30 -0
  37. package/dist/src/lib/ai/backend-url.d.ts +16 -0
  38. package/dist/src/lib/ai/cc-runtime.d.ts +61 -0
  39. package/dist/src/lib/ai/commit-gate.d.ts +21 -0
  40. package/dist/src/lib/ai/context/builder.d.ts +6 -7
  41. package/dist/src/lib/ai/context/compression-middleware.d.ts +15 -0
  42. package/dist/src/lib/ai/context/compressor.d.ts +2 -13
  43. package/dist/src/lib/ai/context/intra-turn-compressor.d.ts +9 -9
  44. package/dist/src/lib/ai/context/optimize-thread-context.d.ts +9 -0
  45. package/dist/src/lib/ai/context/summarizer.d.ts +3 -3
  46. package/dist/src/lib/ai/context/token-estimator.d.ts +5 -8
  47. package/dist/src/lib/ai/fetch.d.ts +1 -1
  48. package/dist/src/lib/ai/finish-turn.d.ts +23 -0
  49. package/dist/src/lib/ai/helpers.d.ts +37 -4
  50. package/dist/src/lib/ai/index.d.ts +6 -3
  51. package/dist/src/lib/ai/inspector.d.ts +41 -0
  52. package/dist/src/lib/ai/interceptors/request-interceptor.d.ts +6 -5
  53. package/dist/src/lib/ai/prompt.d.ts +5 -4
  54. package/dist/src/lib/ai/redis-runs.d.ts +41 -0
  55. package/dist/src/lib/ai/redis-stream-log.d.ts +39 -0
  56. package/dist/src/lib/ai/tools/execute-code.tool.d.ts +18 -4
  57. package/dist/src/lib/ai/tools/query-graph.tool.d.ts +13 -5
  58. package/dist/src/lib/ai/tools/read-attachment.tool.d.ts +21 -0
  59. package/dist/src/lib/ai/tools/search-common.d.ts +17 -0
  60. package/dist/src/lib/ai/tools/search-knowledge.tool.d.ts +15 -0
  61. package/dist/src/lib/ai/tools/search-services.tool.d.ts +12 -0
  62. package/dist/src/lib/ai/tools/search-tools.tool.d.ts +12 -0
  63. package/dist/src/lib/ai/turn-tools.d.ts +39 -0
  64. package/dist/src/lib/ai/vision/rasterize-pdf.d.ts +26 -0
  65. package/dist/src/lib/ai/vision/rasterize-pdf.worker.d.ts +1 -0
  66. package/dist/src/lib/ai/vision/vision-reader.d.ts +45 -0
  67. package/dist/src/lib/cc/client.d.ts +183 -0
  68. package/dist/src/lib/cc/config-cache.d.ts +20 -0
  69. package/dist/src/lib/cc/format.d.ts +23 -0
  70. package/dist/src/lib/cc/registry.d.ts +34 -0
  71. package/dist/src/lib/cc/types.d.ts +27 -0
  72. package/dist/src/lib/config.d.ts +81 -64
  73. package/dist/src/lib/db/drizzle.config.mssql.d.ts +2 -0
  74. package/dist/src/lib/db/drizzle.config.pg.d.ts +2 -0
  75. package/dist/src/lib/db/migrate.d.ts +7 -1
  76. package/dist/src/lib/db/{schema.d.ts → schema.mssql.d.ts} +138 -29
  77. package/dist/src/lib/db/schema.pg.d.ts +546 -0
  78. package/dist/src/lib/factory.d.ts +2 -12
  79. package/dist/src/lib/graph/index.d.ts +10 -11
  80. package/dist/src/lib/graph/resolver.d.ts +2 -3
  81. package/dist/src/lib/index.d.ts +3 -3
  82. package/dist/src/lib/redis.d.ts +19 -0
  83. package/dist/src/lib/routes/chat.d.ts +1 -1
  84. package/dist/src/lib/routes/files.d.ts +1 -1
  85. package/dist/src/lib/routes/owned-thread.d.ts +17 -0
  86. package/dist/src/lib/routes/threads.d.ts +1 -1
  87. package/dist/src/lib/routes/ws.d.ts +1 -1
  88. package/dist/src/lib/types.d.ts +20 -37
  89. package/dist/src/lib/ws/connections.d.ts +2 -1
  90. package/package.json +33 -18
  91. package/src/lib/adapters/database/index.ts +78 -0
  92. package/src/lib/adapters/database/message-content.ts +39 -0
  93. package/src/lib/adapters/database/mssql/attachments.ts +90 -0
  94. package/src/lib/adapters/database/mssql/client.ts +11 -0
  95. package/src/lib/adapters/database/mssql/index.ts +23 -0
  96. package/src/lib/adapters/database/mssql/llm-requests.ts +43 -0
  97. package/src/lib/adapters/database/mssql/messages.ts +92 -0
  98. package/src/lib/adapters/database/mssql/threads.ts +83 -0
  99. package/src/lib/adapters/database/postgres/attachments.ts +88 -0
  100. package/src/lib/adapters/database/postgres/client.ts +11 -0
  101. package/src/lib/adapters/database/postgres/index.ts +23 -0
  102. package/src/lib/adapters/database/postgres/llm-requests.ts +45 -0
  103. package/src/lib/adapters/database/postgres/messages.ts +92 -0
  104. package/src/lib/adapters/database/postgres/threads.ts +84 -0
  105. package/{dist/src/lib/adapters/storage.d.ts → src/lib/adapters/storage/index.ts} +1 -1
  106. package/src/lib/adapters/{minio.ts → storage/minio.ts} +5 -5
  107. package/src/lib/ai/active-runs.ts +157 -0
  108. package/src/lib/ai/attachments.ts +144 -0
  109. package/src/lib/ai/backend-url.ts +27 -0
  110. package/src/lib/ai/cc-runtime.ts +63 -0
  111. package/src/lib/ai/commit-gate.ts +116 -0
  112. package/src/lib/ai/context/builder.ts +12 -14
  113. package/src/lib/ai/context/compression-middleware.ts +44 -0
  114. package/src/lib/ai/context/compressor.ts +67 -59
  115. package/src/lib/ai/context/intra-turn-compressor.ts +121 -204
  116. package/src/lib/ai/context/optimize-thread-context.ts +50 -0
  117. package/src/lib/ai/context/summarizer.ts +28 -30
  118. package/src/lib/ai/context/token-estimator.ts +35 -30
  119. package/src/lib/ai/fetch.ts +10 -5
  120. package/src/lib/ai/finish-turn.ts +75 -0
  121. package/src/lib/ai/helpers.ts +83 -15
  122. package/src/lib/ai/index.ts +359 -329
  123. package/src/lib/ai/inspector.ts +112 -0
  124. package/src/lib/ai/interceptors/request-interceptor.ts +70 -42
  125. package/src/lib/ai/prompt.ts +126 -125
  126. package/src/lib/ai/redis-runs.ts +167 -0
  127. package/src/lib/ai/redis-stream-log.ts +219 -0
  128. package/src/lib/ai/tools/execute-code.tool.ts +178 -67
  129. package/src/lib/ai/tools/query-graph.tool.ts +7 -8
  130. package/src/lib/ai/tools/read-attachment.tool.ts +59 -0
  131. package/src/lib/ai/tools/search-common.ts +15 -0
  132. package/src/lib/ai/tools/search-knowledge.tool.ts +33 -0
  133. package/src/lib/ai/tools/search-services.tool.ts +31 -0
  134. package/src/lib/ai/tools/search-tools.tool.ts +29 -0
  135. package/src/lib/ai/turn-tools.ts +154 -0
  136. package/src/lib/ai/vision/rasterize-pdf.ts +69 -0
  137. package/src/lib/ai/vision/rasterize-pdf.worker.ts +81 -0
  138. package/src/lib/ai/vision/vision-reader.ts +130 -0
  139. package/src/lib/auth/middleware.ts +5 -3
  140. package/src/lib/cc/client.ts +273 -0
  141. package/src/lib/cc/config-cache.ts +32 -0
  142. package/src/lib/cc/format.ts +96 -0
  143. package/src/lib/cc/registry.ts +62 -0
  144. package/src/lib/cc/types.ts +44 -0
  145. package/src/lib/config.ts +102 -71
  146. package/src/lib/db/drizzle.config.mssql.ts +9 -0
  147. package/src/lib/db/drizzle.config.pg.ts +9 -0
  148. package/src/lib/db/migrate.ts +45 -10
  149. package/src/lib/db/migrations/mssql/20260729092726_talented_wilson_fisk/migration.sql +20 -0
  150. package/src/lib/db/migrations/mssql/20260729092726_talented_wilson_fisk/snapshot.json +497 -0
  151. package/src/lib/db/migrations/mssql/20260801003652_perpetual_blue_shield/migration.sql +1 -0
  152. package/src/lib/db/migrations/mssql/20260801003652_perpetual_blue_shield/snapshot.json +516 -0
  153. package/src/lib/db/migrations/mssql/20260812174642_user_id_text/migration.sql +2 -0
  154. package/src/lib/db/migrations/mssql/20260812174642_user_id_text/snapshot.json +516 -0
  155. package/src/lib/db/migrations/pg/20260801084117_thick_ben_grimm/migration.sql +53 -0
  156. package/src/lib/db/migrations/pg/20260801084117_thick_ben_grimm/snapshot.json +584 -0
  157. package/src/lib/db/migrations/pg/20260812174622_user_id_text/migration.sql +2 -0
  158. package/src/lib/db/migrations/pg/20260812174622_user_id_text/snapshot.json +584 -0
  159. package/src/lib/db/{schema.ts → schema.mssql.ts} +29 -18
  160. package/src/lib/db/schema.pg.ts +95 -0
  161. package/src/lib/factory.ts +44 -89
  162. package/src/lib/graph/index.ts +12 -40
  163. package/src/lib/graph/resolver.ts +146 -116
  164. package/src/lib/index.ts +11 -4
  165. package/src/lib/redis.ts +44 -0
  166. package/src/lib/routes/chat.ts +91 -62
  167. package/src/lib/routes/files.ts +105 -83
  168. package/src/lib/routes/owned-thread.ts +17 -0
  169. package/src/lib/routes/threads.ts +14 -30
  170. package/src/lib/routes/ws.ts +9 -3
  171. package/src/lib/types.ts +37 -36
  172. package/src/lib/ws/connections.ts +9 -1
  173. package/tsconfig.json +48 -0
  174. package/dist/src/lib/adapters/database.d.ts +0 -45
  175. package/dist/src/lib/adapters/mssql.d.ts +0 -3
  176. package/dist/src/lib/ai/active-streams.d.ts +0 -14
  177. package/dist/src/lib/ai/context/index.d.ts +0 -16
  178. package/dist/src/lib/ai/tools/capture-files.tool.d.ts +0 -6
  179. package/dist/src/lib/cli/extract-endpoints.d.ts +0 -6
  180. package/dist/src/lib/graph/expand-domains.d.ts +0 -2
  181. package/dist/src/lib/graph/generate-cypher.d.ts +0 -22
  182. package/dist/src/lib/graph/seed.d.ts +0 -19
  183. package/dist/src/lib/graph/validate.d.ts +0 -2
  184. package/dist/src/lib/routes/index.d.ts +0 -4
  185. package/dist/src/lib/ws/events.d.ts +0 -39
  186. package/dist/src/lib/ws/index.d.ts +0 -3
  187. package/dist/src/lib/ws/notify.d.ts +0 -2
  188. package/src/lib/adapters/database.ts +0 -48
  189. package/src/lib/adapters/mssql.ts +0 -231
  190. package/src/lib/ai/active-streams.ts +0 -123
  191. package/src/lib/ai/context/index.ts +0 -80
  192. package/src/lib/ai/tools/capture-files.tool.ts +0 -20
  193. package/src/lib/cli/extract-endpoints.ts +0 -550
  194. package/src/lib/graph/expand-domains.ts +0 -276
  195. package/src/lib/graph/generate-cypher.ts +0 -192
  196. package/src/lib/graph/seed.ts +0 -162
  197. package/src/lib/graph/validate.ts +0 -78
  198. package/src/lib/routes/index.ts +0 -4
  199. package/src/lib/ws/events.ts +0 -39
  200. package/src/lib/ws/index.ts +0 -11
  201. package/src/lib/ws/notify.ts +0 -9
  202. package/src/sample/db/client.ts +0 -9
  203. package/src/sample/db/migrations/20260411023125_rich_purple_man/migration.sql +0 -5
  204. package/src/sample/db/migrations/20260411023125_rich_purple_man/snapshot.json +0 -50
  205. package/src/sample/db/schema.ts +0 -10
  206. package/src/sample/domains/account/concepts/session.concept.ts +0 -17
  207. package/src/sample/domains/account/endpoints/listCurrentSessions.endpoint.ts +0 -31
  208. package/src/sample/domains/account/endpoints/revokeAllOtherSessions.endpoint.ts +0 -22
  209. package/src/sample/domains/account/endpoints/revokeCurrentSession.endpoint.ts +0 -33
  210. package/src/sample/domains/account/index.ts +0 -18
  211. package/src/sample/domains/appraisals/concepts/appraisalCycle.concept.ts +0 -15
  212. package/src/sample/domains/appraisals/concepts/appraisalRecord.concept.ts +0 -20
  213. package/src/sample/domains/appraisals/concepts/appraisalRecordCompetency.concept.ts +0 -10
  214. package/src/sample/domains/appraisals/concepts/appraisalRecordFunctionalRequirement.concept.ts +0 -10
  215. package/src/sample/domains/appraisals/concepts/appraisalRecordGoal.concept.ts +0 -17
  216. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordById.endpoint.ts +0 -321
  217. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordCompetencies.endpoint.ts +0 -120
  218. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordFunctionalRequirements.endpoint.ts +0 -126
  219. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordGoals.endpoint.ts +0 -104
  220. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordScore.endpoint.ts +0 -58
  221. package/src/sample/domains/appraisals/endpoints/listAppraisalCyclesSimple.endpoint.ts +0 -52
  222. package/src/sample/domains/appraisals/endpoints/listAppraisalRecords.endpoint.ts +0 -245
  223. package/src/sample/domains/appraisals/endpoints/listAppraisalTemplatesSimple.endpoint.ts +0 -52
  224. package/src/sample/domains/appraisals/index.ts +0 -40
  225. package/src/sample/domains/appraisals/rules/appraisalRecordScoping.rule.ts +0 -7
  226. package/src/sample/domains/attendance/concepts/attendancePermission.concept.ts +0 -17
  227. package/src/sample/domains/attendance/concepts/attendanceTransaction.concept.ts +0 -17
  228. package/src/sample/domains/attendance/concepts/publicHoliday.concept.ts +0 -10
  229. package/src/sample/domains/attendance/concepts/punch.concept.ts +0 -19
  230. package/src/sample/domains/attendance/endpoints/getAttendanceTransactionById.endpoint.ts +0 -444
  231. package/src/sample/domains/attendance/endpoints/getCurrentAttendanceProfile.endpoint.ts +0 -886
  232. package/src/sample/domains/attendance/endpoints/getCurrentAttendanceStatistics.endpoint.ts +0 -49
  233. package/src/sample/domains/attendance/endpoints/listAttendancePermissions.endpoint.ts +0 -176
  234. package/src/sample/domains/attendance/endpoints/listAttendanceTransactionStates.endpoint.ts +0 -26
  235. package/src/sample/domains/attendance/endpoints/listAttendanceTransactions.endpoint.ts +0 -373
  236. package/src/sample/domains/attendance/endpoints/listPublicHolidaysSimple.endpoint.ts +0 -52
  237. package/src/sample/domains/attendance/endpoints/listPunchTypes.endpoint.ts +0 -25
  238. package/src/sample/domains/attendance/endpoints/listPunches.endpoint.ts +0 -178
  239. package/src/sample/domains/attendance/index.ts +0 -41
  240. package/src/sample/domains/attendance/services/attendancePermissions.service.ts +0 -15
  241. package/src/sample/domains/employees/concepts/employee.concept.ts +0 -20
  242. package/src/sample/domains/employees/concepts/employeeBankAccount.concept.ts +0 -19
  243. package/src/sample/domains/employees/concepts/employeeChild.concept.ts +0 -19
  244. package/src/sample/domains/employees/concepts/employeeDocument.concept.ts +0 -21
  245. package/src/sample/domains/employees/concepts/employeeEvent.concept.ts +0 -21
  246. package/src/sample/domains/employees/concepts/employeeExperience.concept.ts +0 -18
  247. package/src/sample/domains/employees/concepts/employeeJobLevelChange.concept.ts +0 -21
  248. package/src/sample/domains/employees/concepts/employeeMedal.concept.ts +0 -20
  249. package/src/sample/domains/employees/concepts/employeeQualification.concept.ts +0 -20
  250. package/src/sample/domains/employees/concepts/employeeSpouse.concept.ts +0 -21
  251. package/src/sample/domains/employees/concepts/employeeViolation.concept.ts +0 -19
  252. package/src/sample/domains/employees/endpoints/getCurrentEmployee.endpoint.ts +0 -1651
  253. package/src/sample/domains/employees/endpoints/getCurrentIdentity.endpoint.ts +0 -197
  254. package/src/sample/domains/employees/endpoints/getCurrentManagers.endpoint.ts +0 -142
  255. package/src/sample/domains/employees/endpoints/getEmployeeById.endpoint.ts +0 -1659
  256. package/src/sample/domains/employees/endpoints/getEmployeeStatistics.endpoint.ts +0 -43
  257. package/src/sample/domains/employees/endpoints/listBankAccounts.endpoint.ts +0 -223
  258. package/src/sample/domains/employees/endpoints/listChildren.endpoint.ts +0 -211
  259. package/src/sample/domains/employees/endpoints/listDocuments.endpoint.ts +0 -222
  260. package/src/sample/domains/employees/endpoints/listEmployeesSimple.endpoint.ts +0 -156
  261. package/src/sample/domains/employees/endpoints/listEvents.endpoint.ts +0 -120
  262. package/src/sample/domains/employees/endpoints/listExperiences.endpoint.ts +0 -188
  263. package/src/sample/domains/employees/endpoints/listJobLevelChanges.endpoint.ts +0 -240
  264. package/src/sample/domains/employees/endpoints/listMedals.endpoint.ts +0 -266
  265. package/src/sample/domains/employees/endpoints/listQualifications.endpoint.ts +0 -265
  266. package/src/sample/domains/employees/endpoints/listSpouses.endpoint.ts +0 -278
  267. package/src/sample/domains/employees/endpoints/listViolations.endpoint.ts +0 -245
  268. package/src/sample/domains/employees/index.ts +0 -75
  269. package/src/sample/domains/employees/services/employees.service.ts +0 -181
  270. package/src/sample/domains/index.ts +0 -10
  271. package/src/sample/domains/leaves/concepts/leave.concept.ts +0 -21
  272. package/src/sample/domains/leaves/concepts/leaveBalance.concept.ts +0 -17
  273. package/src/sample/domains/leaves/concepts/leaveBalanceTransaction.concept.ts +0 -16
  274. package/src/sample/domains/leaves/concepts/leaveType.concept.ts +0 -13
  275. package/src/sample/domains/leaves/concepts/leaveTypes.concept.ts +0 -205
  276. package/src/sample/domains/leaves/endpoints/getBalance.endpoint.ts +0 -53
  277. package/src/sample/domains/leaves/endpoints/getLeaveById.endpoint.ts +0 -53
  278. package/src/sample/domains/leaves/endpoints/listBalanceTransactions.endpoint.ts +0 -196
  279. package/src/sample/domains/leaves/endpoints/listBalanceTypes.endpoint.ts +0 -26
  280. package/src/sample/domains/leaves/endpoints/listBalances.endpoint.ts +0 -201
  281. package/src/sample/domains/leaves/endpoints/listLeaveTypes.endpoint.ts +0 -28
  282. package/src/sample/domains/leaves/endpoints/listLeaves.endpoint.ts +0 -234
  283. package/src/sample/domains/leaves/index.ts +0 -38
  284. package/src/sample/domains/leaves/services/leaveRequests.service.ts +0 -232
  285. package/src/sample/domains/notifications/concepts/notification.concept.ts +0 -19
  286. package/src/sample/domains/notifications/endpoints/countMyNotifications.endpoint.ts +0 -21
  287. package/src/sample/domains/notifications/endpoints/getMyNotification.endpoint.ts +0 -57
  288. package/src/sample/domains/notifications/endpoints/listMyNotifications.endpoint.ts +0 -59
  289. package/src/sample/domains/notifications/endpoints/markMyNotificationAsRead.endpoint.ts +0 -32
  290. package/src/sample/domains/notifications/index.ts +0 -20
  291. package/src/sample/domains/payroll/concepts/payslip.concept.ts +0 -18
  292. package/src/sample/domains/payroll/concepts/salaryCertificate.concept.ts +0 -17
  293. package/src/sample/domains/payroll/concepts/salaryCertificateRequestingEntity.concept.ts +0 -8
  294. package/src/sample/domains/payroll/endpoints/getPayslipById.endpoint.ts +0 -238
  295. package/src/sample/domains/payroll/endpoints/listPayslips.endpoint.ts +0 -216
  296. package/src/sample/domains/payroll/endpoints/listSalaryCertificateLanguages.endpoint.ts +0 -25
  297. package/src/sample/domains/payroll/endpoints/listSalaryCertificateRequestingEntities.endpoint.ts +0 -62
  298. package/src/sample/domains/payroll/endpoints/listSalaryCertificateTypes.endpoint.ts +0 -24
  299. package/src/sample/domains/payroll/endpoints/listSalaryCertificates.endpoint.ts +0 -122
  300. package/src/sample/domains/payroll/endpoints/previewSalaryCertificate.endpoint.ts +0 -45
  301. package/src/sample/domains/payroll/index.ts +0 -32
  302. package/src/sample/domains/payroll/rules/payrollEmployeeContext.rule.ts +0 -7
  303. package/src/sample/domains/payroll/services/salaryCertificates.service.ts +0 -15
  304. package/src/sample/domains/servicing/concepts/service.concept.ts +0 -6
  305. package/src/sample/domains/servicing/concepts/serviceRequest.concept.ts +0 -12
  306. package/src/sample/domains/servicing/concepts/serviceRequestApproval.concept.ts +0 -9
  307. package/src/sample/domains/servicing/endpoints/cancelServiceRequest.endpoint.ts +0 -21
  308. package/src/sample/domains/servicing/endpoints/createServiceRequest.endpoint.ts +0 -511
  309. package/src/sample/domains/servicing/endpoints/getServiceRequest.endpoint.ts +0 -352
  310. package/src/sample/domains/servicing/endpoints/getServiceRequestFormItems.endpoint.ts +0 -219
  311. package/src/sample/domains/servicing/endpoints/getServiceRequestTransactionFormItems.endpoint.ts +0 -216
  312. package/src/sample/domains/servicing/endpoints/getServiceStates.endpoint.ts +0 -47
  313. package/src/sample/domains/servicing/endpoints/invokeServiceRequestAction.endpoint.ts +0 -139
  314. package/src/sample/domains/servicing/endpoints/listServiceRequests.endpoint.ts +0 -292
  315. package/src/sample/domains/servicing/endpoints/listServices.endpoint.ts +0 -108
  316. package/src/sample/domains/servicing/endpoints/updateServiceRequest.endpoint.ts +0 -355
  317. package/src/sample/domains/servicing/index.ts +0 -44
  318. package/src/sample/domains/servicing/rules/checkValidActions.rule.ts +0 -7
  319. package/src/sample/domains/servicing/rules/createServiceRequest.rule.ts +0 -7
  320. package/src/sample/domains/servicing/rules/filterServiceRequestsByState.rule.ts +0 -7
  321. package/src/sample/domains/servicing/rules/passingOptionFields.rule.ts +0 -7
  322. package/src/sample/domains/shared/rules/pagination.rule.ts +0 -7
  323. package/src/sample/domains/suggestions/concepts/suggestion.concept.ts +0 -19
  324. package/src/sample/domains/suggestions/concepts/suggestionCategory.concept.ts +0 -7
  325. package/src/sample/domains/suggestions/concepts/suggestionImpactLevel.concept.ts +0 -7
  326. package/src/sample/domains/suggestions/endpoints/createSuggestion.endpoint.ts +0 -705
  327. package/src/sample/domains/suggestions/endpoints/deleteSuggestion.endpoint.ts +0 -32
  328. package/src/sample/domains/suggestions/endpoints/getSuggestionById.endpoint.ts +0 -635
  329. package/src/sample/domains/suggestions/endpoints/listSuggestionCategories.endpoint.ts +0 -62
  330. package/src/sample/domains/suggestions/endpoints/listSuggestionImpactLevels.endpoint.ts +0 -62
  331. package/src/sample/domains/suggestions/endpoints/listSuggestionStates.endpoint.ts +0 -24
  332. package/src/sample/domains/suggestions/endpoints/listSuggestions.endpoint.ts +0 -132
  333. package/src/sample/domains/suggestions/endpoints/updateSuggestion.endpoint.ts +0 -690
  334. package/src/sample/domains/suggestions/endpoints/updateSuggestionState.endpoint.ts +0 -39
  335. package/src/sample/domains/suggestions/index.ts +0 -34
  336. package/src/sample/domains/suggestions/rules/suggestionScope.rule.ts +0 -7
  337. package/src/sample/domains/surveys/concepts/survey.concept.ts +0 -10
  338. package/src/sample/domains/surveys/concepts/surveyResponse.concept.ts +0 -14
  339. package/src/sample/domains/surveys/endpoints/getSurveyResponseById.endpoint.ts +0 -131
  340. package/src/sample/domains/surveys/endpoints/getTargetedSurvey.endpoint.ts +0 -129
  341. package/src/sample/domains/surveys/endpoints/listTargetedSurveys.endpoint.ts +0 -160
  342. package/src/sample/domains/surveys/endpoints/respondToSurvey.endpoint.ts +0 -144
  343. package/src/sample/domains/surveys/index.ts +0 -21
  344. package/src/sample/drizzle.config.ts +0 -11
  345. package/src/sample/index.bak.ts +0 -341
  346. package/src/sample/index.ts +0 -318
  347. package/src/sample/official-statements/defaults.ts +0 -57
  348. package/src/sample/official-statements/routes.ts +0 -137
  349. package/src/sample/official-statements/service.ts +0 -148
  350. package/src/sample/official-statements/types.ts +0 -23
  351. /package/src/lib/db/migrations/{20260326231647_nice_speedball → mssql/20260326231647_nice_speedball}/migration.sql +0 -0
  352. /package/src/lib/db/migrations/{20260326231647_nice_speedball → mssql/20260326231647_nice_speedball}/snapshot.json +0 -0
@@ -1,324 +1,363 @@
1
1
  import {
2
+ chat,
3
+ chatParamsFromRequest,
4
+ convertMessagesToModelMessages,
5
+ generateMessageId,
6
+ maxIterations,
7
+ mergeAgentTools,
8
+ normalizeToUIMessage,
9
+ resumeServerSentEventsResponse,
10
+ toServerSentEventsResponse,
2
11
  type UIMessage,
3
- type ToolSet,
4
- convertToModelMessages,
5
- generateId,
6
- generateText,
7
- safeValidateUIMessages,
8
- stepCountIs,
9
- streamText,
10
- wrapLanguageModel,
11
- } from "ai";
12
+ } from "@tanstack/ai";
12
13
  import { HTTPException } from "hono/http-exception";
13
- import type { ResolvedCortexAgentConfig } from "../config.ts";
14
- import type { MessageMetadata, Thread, TokenUsage } from "../types.ts";
15
- import { createModel, createEmbeddingModel } from "./helpers.ts";
16
- import { buildSystemPrompt, resolveSession } from "./prompt.ts";
17
- import { createQueryGraphTool } from "./tools/query-graph.tool.ts";
18
- import { createExecuteCodeTool } from "./tools/execute-code.tool.ts";
19
- import { captureFilesTool } from "./tools/capture-files.tool.ts";
20
- import { createRequestInterceptor } from "./interceptors/request-interceptor.ts";
21
- import { createNeo4jClient } from "../graph/neo4j.ts";
22
- import { resolveFromGraph } from "../graph/resolver.ts";
23
- import { notify } from "../ws/index.ts";
24
- import { buildContextMessages } from "./context/builder.ts";
25
- import { optimizeThreadContext, estimateTokens, trimMessagesToFit } from "./context/index.ts";
14
+ import type { ResolvedCortexAgentConfig, ToolSet } from "@/config";
15
+ import type { ChatMessage, MessageMetadata, Thread } from "@/types";
16
+ import { createModel } from "./helpers";
17
+ import { buildSystemPrompt, resolveSession } from "./prompt";
18
+ import { ControlCenterClient } from "@/cc/client";
19
+ import { getControlCenterConfig } from "@/cc/config-cache";
20
+ import { buildPromptVariables } from "@/cc/format";
21
+ import { getRecentCcTools } from "@/cc/registry";
22
+ import { createEmbedder, createNeo4jClient } from "@cortex/contracts/graph";
23
+ import { resolveFromGraph } from "@/graph/resolver";
24
+ import { notify } from "@/ws/connections";
25
+ import { buildContextMessages, trimMessagesToFit } from "./context/builder";
26
+ import { estimateTokens } from "./context/token-estimator";
27
+ import { createCompressionMiddleware } from "./context/compression-middleware";
28
+ import { createCcRuntime } from "./cc-runtime";
26
29
  import {
27
- compressIntraTurnToolResults,
28
- estimateToolResultTokens,
29
- summarizeOldStepResults,
30
- } from "./context/intra-turn-compressor.ts";
31
- import {
32
- registerStream,
33
- attachSseStream,
34
- removeStream,
35
- isStreamRunning,
36
- } from "./active-streams.ts";
37
- import { toThreadSummary } from "../types.ts";
38
-
39
- export async function stream(
40
- messages: unknown[],
30
+ buildTurnTools,
31
+ createToolInstrumentation,
32
+ hasDefaultAttachmentInterceptor,
33
+ } from "./turn-tools";
34
+ import { createInspector } from "./inspector";
35
+ import { commitBeforeTerminal } from "./commit-gate";
36
+ import { finishTurn } from "./finish-turn";
37
+ import { endRun, isRunning, joinLog, producerLog, startRun } from "./active-runs";
38
+ import { toThreadSummary } from "@/types";
39
+ import { buildAttachmentListing, prepareAttachmentsForTurn } from "./attachments";
40
+
41
+ /** The AG-UI request body, as the route parsed it. */
42
+ export type TurnParams = Awaited<ReturnType<typeof chatParamsFromRequest>>;
43
+
44
+ /** Cortex's agentic loop cap; the SDK's own default is 5. */
45
+ const MAX_ITERATIONS = 25;
46
+
47
+ export async function startTurn(
48
+ params: TurnParams,
41
49
  thread: Thread,
42
50
  userId: string,
43
51
  token: string,
44
52
  requestContext: Record<string, unknown>,
45
53
  config: ResolvedCortexAgentConfig,
46
54
  ) {
47
- const abortController = new AbortController();
48
-
49
- const validationResult = await safeValidateUIMessages({ messages });
50
- if (!validationResult.success) {
51
- throw new HTTPException(423, { message: "Invalid messages format" });
52
- }
53
-
54
- const validatedMessages = validationResult.data;
55
- await config.db.messages.upsert(thread.id, validatedMessages);
56
- const updatedThread = await config.db.threads.touch(thread.id);
55
+ const run = await startRun(thread.id);
56
+ try {
57
+ // The client owns its own messages, and the assistant turn it sends back
58
+ // once a client-side tool has answered — without that answer stored, the
59
+ // next turn replays an assistant message whose tool calls nothing ever
60
+ // resolved, and the provider rejects the thread from there on. Everything
61
+ // else the server generated stays the server's; the repository keeps the
62
+ // usage and model id only it ever knew.
63
+ const incoming = params.messages
64
+ .map((message) => toChatMessage(normalizeToUIMessage(message, generateMessageId)))
65
+ .filter((message) => message.role === "user" || answersToolCalls(message));
66
+ await config.db.messages.upsert(thread.id, incoming);
67
+
68
+ const lastUserMessage = incoming.findLast((message) => message.role === "user");
69
+ const prompt = textOf(lastUserMessage);
70
+ if (thread.title === null && prompt) {
71
+ void generateTitle(thread.id, prompt, userId, config);
72
+ }
57
73
 
58
- const activeStream = registerStream(thread.id, abortController);
74
+ const updatedThread = await config.db.threads.touch(thread.id);
59
75
 
60
- notify(userId, thread.agentId, {
61
- type: "thread:run-started",
62
- payload: { thread: toThreadSummary(updatedThread, true) },
63
- });
76
+ // A stop can land while the claim exists but before the run is
77
+ // announced; announcing anyway would put run-started after the abort's
78
+ // run-finished and pin the thread as running on every other tab. No
79
+ // await sits between this check and the notify, so once it passes the
80
+ // abort's run-finished can only follow the announcement.
81
+ if (run.abortController.signal.aborted) {
82
+ releaseRun(thread.id, run.runId);
83
+ return null;
84
+ }
64
85
 
65
- // Extract prompt from the just-upserted messages (last user message)
66
- // so we can start graph resolution without waiting for history fetch
67
- const prompt =
68
- validatedMessages
69
- .filter((x) => x.role === "user")
70
- .at(-1)
71
- ?.parts.find((x) => x.type === "text")?.text ?? "";
72
-
73
- const model = createModel(config.model);
74
- const embeddingModel = createEmbeddingModel(config.embedding);
75
- const neo4j = config.neo4j ? createNeo4jClient(config.neo4j, embeddingModel) : undefined;
76
-
77
- // Run independent operations in parallel
78
- const [contextResult, resolved, session] = await Promise.all([
79
- // Branch A: Load messages + build token-aware context window
80
- buildContextMessages(userId, thread, config.db, config.context),
81
- // Branch B: Resolve graph context (400-2000ms, the bottleneck)
82
- neo4j
83
- ? resolveFromGraph(prompt, {
84
- neo4j,
85
- embeddingModel,
86
- reranker: config.reranker,
86
+ notify(userId, thread.agentId, {
87
+ type: "thread:run-started",
88
+ payload: { thread: toThreadSummary(updatedThread, true) },
89
+ });
90
+
91
+ const threadAttachments = lastUserMessage
92
+ ? await prepareAttachmentsForTurn({
93
+ config,
94
+ threadId: thread.id,
95
+ userId,
96
+ message: lastUserMessage,
97
+ abortSignal: run.abortController.signal,
87
98
  })
88
- : Promise.resolve(null),
89
- // Branch C: Resolve session data
90
- resolveSession(config, thread, token),
91
- ]);
99
+ : await config.db.attachments.listByThread(thread.id, userId);
92
100
 
93
- const { messages: contextMessages, allMessages: originalMessages } = contextResult;
101
+ // Claiming rewrites the message the user just sent, and the tabs that did
102
+ // not send it are already showing that message — with the run underway,
103
+ // this is their only chance to learn which files it carries.
104
+ if (threadAttachments.some((attachment) => attachment.messageId === lastUserMessage?.id)) {
105
+ notify(userId, thread.agentId, {
106
+ type: "thread:messages-updated",
107
+ payload: { threadId: thread.id, thread: toThreadSummary(updatedThread, true) },
108
+ });
109
+ }
94
110
 
95
- // Build tools
96
- const builtInTools: ToolSet = {
97
- captureFiles: captureFilesTool,
98
- };
111
+ const turnKey = lastUserMessage?.id ?? "0";
112
+
113
+ const inspector = createInspector();
114
+ const { model, embed, neo4j, ccClient } = createTurnProviders(config, inspector.fetch);
115
+
116
+ // Run independent operations in parallel
117
+ const [contextResult, resolved, session, ccConfig, ccResolved] = await Promise.all([
118
+ // Branch A: Load messages + build token-aware context window
119
+ buildContextMessages(userId, thread, config.db, config.context),
120
+ // Branch B: Resolve graph context (400-2000ms, the bottleneck)
121
+ neo4j && embed
122
+ ? resolveFromGraph(prompt, { neo4j, embed, reranker: config.reranker })
123
+ : Promise.resolve(null),
124
+ // Branch C: Resolve session data
125
+ resolveSession(config, thread, token),
126
+ // Branch D: Control Center config (ETag-revalidated, last-known-good on outage)
127
+ ccClient
128
+ ? getControlCenterConfig(ccClient, config.agentId, run.abortController.signal)
129
+ : Promise.resolve(null),
130
+ // Branch E: Control Center /resolve — on failure the turn degrades to
131
+ // the search meta-tools, it never blocks
132
+ ccClient && prompt
133
+ ? ccClient.resolve(
134
+ config.agentId,
135
+ {
136
+ query: prompt,
137
+ hints: {
138
+ threadId: thread.id,
139
+ recentToolNames: getRecentCcTools(thread.id),
140
+ },
141
+ },
142
+ run.abortController.signal,
143
+ )
144
+ : Promise.resolve(null),
145
+ ]);
146
+
147
+ const cc = createCcRuntime({
148
+ ccClient,
149
+ ccConfig,
150
+ ccResolved,
151
+ config,
152
+ thread,
153
+ userId,
154
+ token,
155
+ turnKey,
156
+ abortSignal: run.abortController.signal,
157
+ getStepIndex: () => inspector.steps.length,
158
+ onToolProgress: (toolName, toolCallId, status) =>
159
+ emitToolProgress(userId, thread, toolName, toolCallId, status),
160
+ });
161
+
162
+ const { messages: contextMessages, allMessages } = contextResult;
163
+
164
+ const turnToolsOptions = {
165
+ config,
166
+ cc,
167
+ neo4j,
168
+ thread,
169
+ userId,
170
+ token,
171
+ session,
172
+ requestContext,
173
+ threadAttachments,
174
+ };
175
+ const tools = buildTurnTools(turnToolsOptions);
176
+ const instrumentation = createToolInstrumentation({
177
+ ...turnToolsOptions,
178
+ onToolStart: (toolName, toolCallId) =>
179
+ emitToolProgress(userId, thread, toolName, toolCallId, "started"),
180
+ onToolFinish: (toolName, toolCallId) =>
181
+ emitToolProgress(userId, thread, toolName, toolCallId, "finished"),
182
+ });
183
+
184
+ const systemPrompt = await buildSystemPrompt(
185
+ config,
186
+ resolved,
187
+ { session, requestContext },
188
+ cc
189
+ ? {
190
+ config: cc.config,
191
+ resolved: ccResolved,
192
+ variables: buildPromptVariables(cc.config.promptVariables, {
193
+ ...session,
194
+ ...requestContext,
195
+ locale: cc.locale,
196
+ }),
197
+ }
198
+ : undefined,
199
+ buildAttachmentListing(threadAttachments, lastUserMessage?.id, {
200
+ readAttachment: tools.some((tool) => tool.name === "readAttachment"),
201
+ attachmentSentinels: hasDefaultAttachmentInterceptor(config),
202
+ }),
203
+ );
204
+
205
+ const stream = chat({
206
+ adapter: model,
207
+ // Client tool declarations ride in on every request, so the server
208
+ // never re-declares them.
209
+ tools: mergeAgentTools(tools, params.tools),
210
+ messages: convertMessagesToModelMessages(
211
+ fitToContextWindow(contextMessages, systemPrompt, tools, config),
212
+ ),
213
+ systemPrompts: [systemPrompt],
214
+ agentLoopStrategy: maxIterations(MAX_ITERATIONS),
215
+ abortController: run.abortController,
216
+ middleware: [
217
+ createCompressionMiddleware(config),
218
+ ...(instrumentation ? [instrumentation] : []),
219
+ inspector.middleware,
220
+ ],
221
+ threadId: thread.id,
222
+ runId: run.runId,
223
+ });
224
+
225
+ const producer = toServerSentEventsResponse(
226
+ commitBeforeTerminal(stream, {
227
+ isAborted: () => run.abortController.signal.aborted,
228
+ commit: async function (produced) {
229
+ const messages = produced.map(toChatMessage).map(withTurnMetadata);
230
+ await finishTurn({
231
+ messages,
232
+ // Summarization and the consumer hook judge the thread, not
233
+ // the turn — the run's own output is only its tail.
234
+ threadMessages: [...allMessages, ...messages],
235
+ isAborted: run.abortController.signal.aborted,
236
+ assistantMessageId: messages.at(-1)?.id,
237
+ thread,
238
+ userId,
239
+ capturedSteps: inspector.steps,
240
+ config,
241
+ });
242
+ },
243
+ }),
244
+ {
245
+ // Append per chunk rather than per batch of 32: a client joining
246
+ // the log is waiting on real tokens, not on a buffer filling up.
247
+ durability: { adapter: producerLog(run.runId), batch: 1 },
248
+ abortController: run.abortController,
249
+ },
250
+ );
251
+
252
+ function withTurnMetadata(message: ChatMessage, index: number, all: ChatMessage[]) {
253
+ if (index !== all.length - 1 || message.role !== "assistant") return message;
254
+
255
+ return {
256
+ ...message,
257
+ metadata: {
258
+ modelId: inspector.modelId,
259
+ isAborted: run.abortController.signal.aborted,
260
+ tokenUsage: inspector.tokenUsage,
261
+ } satisfies MessageMetadata,
262
+ };
263
+ }
99
264
 
100
- if (neo4j) {
101
- builtInTools["queryGraph"] = createQueryGraphTool(neo4j);
265
+ // Generation outlives the request that started it, so a reload mid-turn
266
+ // rejoins the log instead of restarting the model. Draining into a sink
267
+ // that never cancels is what detaches the producer from this response;
268
+ // the client gets a reader on the same log.
269
+ void producer
270
+ .body!.pipeTo(new WritableStream())
271
+ .catch(() => undefined)
272
+ .finally(() => releaseRun(thread.id, run.runId));
273
+
274
+ return resumeServerSentEventsResponse({ adapter: joinLog(thread.id, run.runId) });
275
+ } catch (error) {
276
+ releaseRun(thread.id, run.runId);
277
+ throw error;
102
278
  }
279
+ }
103
280
 
104
- if (config.backendFetch) {
105
- const backendFetchWithInterceptor = {
106
- ...config.backendFetch,
107
- transformRequestBody:
108
- config.backendFetch.transformRequestBody ??
109
- createRequestInterceptor(
110
- config.db,
111
- config.storage,
112
- config.backendFetch.interceptor,
113
- ),
114
- };
281
+ /** Releasing the claim must never mask the turn's own error or reject unhandled. */
282
+ function releaseRun(threadId: string, runId: string) {
283
+ endRun(threadId, runId).catch((error: unknown) =>
284
+ console.error("[cortex-server] Failed to release run claim:", error),
285
+ );
286
+ }
115
287
 
116
- builtInTools["executeCode"] = createExecuteCodeTool(backendFetchWithInterceptor, token);
117
- }
288
+ /** Drops the SDK's transport shape into the envelope cortex persists and serves. */
289
+ function toChatMessage(message: UIMessage) {
290
+ return { id: message.id, role: message.role, parts: message.parts } satisfies ChatMessage;
291
+ }
118
292
 
119
- const resolvedUserTools =
120
- typeof config.tools === "function"
121
- ? config.tools({ thread, userId, token, session, requestContext })
122
- : config.tools;
293
+ /**
294
+ * An assistant turn the client is sending back because it resolved a tool call.
295
+ *
296
+ * The wire fans that turn out into a `role: "tool"` message per result, and each
297
+ * of those normalizes back into an assistant message carrying only the result,
298
+ * under a freshly generated id. Stored as rows of their own they would replay as
299
+ * assistant turns answering calls that no message makes, so the turn is picked
300
+ * out by holding both halves of the exchange.
301
+ */
302
+ function answersToolCalls(message: ChatMessage) {
303
+ return (
304
+ message.role === "assistant" &&
305
+ message.parts.some((part) => part.type === "tool-call") &&
306
+ message.parts.some((part) => part.type === "tool-result")
307
+ );
308
+ }
123
309
 
124
- const tools = {
125
- ...builtInTools,
126
- ...resolvedUserTools,
127
- } as ToolSet;
310
+ function textOf(message: ChatMessage | undefined) {
311
+ const part = message?.parts.find((candidate) => candidate.type === "text");
312
+ return part?.type === "text" ? part.content : "";
313
+ }
128
314
 
129
- const systemPrompt = await buildSystemPrompt(config, resolved, {
130
- session,
131
- requestContext,
132
- });
315
+ /**
316
+ * Builds the clients one turn needs. Neo4j runs vector search through an embedding
317
+ * model, so configuring a graph without one is a misconfiguration we surface
318
+ * loudly rather than quietly serving every turn with no graph context.
319
+ */
320
+ function createTurnProviders(config: ResolvedCortexAgentConfig, fetch: typeof globalThis.fetch) {
321
+ if (config.neo4j && !config.embedding) {
322
+ throw new HTTPException(500, {
323
+ message: "embedding config is required when neo4j is configured",
324
+ });
325
+ }
133
326
 
134
- // The context builder reserved a static token budget for the system prompt + tools.
135
- // Now that we have the actual values, verify the reserve was sufficient and trim
136
- // the oldest messages if it wasn't.
327
+ const embed = config.embedding ? createEmbedder(config.embedding) : undefined;
328
+
329
+ return {
330
+ // The inspector's fetch is per-turn, so the model is too.
331
+ model: createModel(config.model, { fetch }),
332
+ embed,
333
+ neo4j: config.neo4j && embed ? createNeo4jClient(config.neo4j, embed) : undefined,
334
+ ccClient: config.controlCenter ? new ControlCenterClient(config.controlCenter) : null,
335
+ };
336
+ }
337
+
338
+ /**
339
+ * The context builder reserved a static token budget for the system prompt and
340
+ * tools before either existed. Now that both are real, drop the oldest messages
341
+ * if the reserve fell short.
342
+ */
343
+ function fitToContextWindow(
344
+ contextMessages: ChatMessage[],
345
+ systemPrompt: string,
346
+ tools: ToolSet,
347
+ config: ResolvedCortexAgentConfig,
348
+ ) {
137
349
  const actualFixedCost = estimateTokens(systemPrompt) + estimateTokens(JSON.stringify(tools));
138
350
  const { reservedTokenBudget, maxContextTokens } = config.context;
139
- const trimmedMessages =
140
- actualFixedCost > reservedTokenBudget
141
- ? trimMessagesToFit(contextMessages, maxContextTokens - actualFixedCost)
142
- : contextMessages;
143
-
144
- const recentMessages = await convertToModelMessages(trimmedMessages);
145
-
146
- const { context: contextConfig } = config;
147
-
148
- const capturedSteps: {
149
- prompt: string;
150
- output: string | null;
151
- tokenUsage: TokenUsage | null;
152
- }[] = [];
153
-
154
- const result = streamText({
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
- }),
186
- system: systemPrompt,
187
- tools,
188
- messages: recentMessages,
189
- abortSignal: abortController.signal,
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
- },
222
- });
351
+ if (actualFixedCost <= reservedTokenBudget) return contextMessages;
223
352
 
224
- return result.toUIMessageStreamResponse<UIMessage<MessageMetadata>>({
225
- originalMessages,
226
- generateMessageId: generateId,
227
- consumeSseStream: ({ stream: sseStream }) => {
228
- attachSseStream(thread.id, sseStream);
229
- },
230
- onFinish: async ({ messages: finishedMessages, isAborted }) => {
231
- if (isAborted) {
232
- finalizeAbortedMessages(finishedMessages);
233
- }
234
-
235
- // Record token usage (result promises reject on abort, so skip)
236
- const lastAssistantMessage = finishedMessages
237
- .filter((x) => x.role === "assistant")
238
- .at(-1);
239
- if (lastAssistantMessage && !isAborted) {
240
- const providerMetadata = await result.providerMetadata;
241
- const response = await result.response;
242
- const usage = await result.totalUsage;
243
- let metadata: MessageMetadata = {
244
- isAborted,
245
- providerMetadata,
246
- modelId: response.modelId,
247
- tokenUsage: {
248
- input: {
249
- noCache: usage.inputTokenDetails.noCacheTokens ?? 0,
250
- cacheRead: usage.inputTokenDetails.cacheReadTokens ?? 0,
251
- cacheWrite: usage.inputTokenDetails.cacheWriteTokens ?? 0,
252
- total: usage.inputTokens ?? 0,
253
- },
254
- output: {
255
- reasoning: usage.outputTokenDetails.reasoningTokens ?? 0,
256
- text: usage.outputTokenDetails.textTokens ?? 0,
257
- total: usage.outputTokens ?? 0,
258
- },
259
- total: usage.totalTokens ?? 0,
260
- },
261
- };
262
-
263
- lastAssistantMessage.metadata = metadata;
264
- } else if (lastAssistantMessage) {
265
- lastAssistantMessage.metadata = {
266
- isAborted,
267
- modelId: "",
268
- providerMetadata: undefined,
269
- };
270
- }
271
-
272
- const persistedThread = await config.db.threads.getById(userId, thread.id);
273
- if (!persistedThread) {
274
- removeStream(thread.id);
275
- return;
276
- }
277
-
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
-
297
- config.onStreamFinish?.({ messages: finishedMessages, isAborted });
298
-
299
- // XXX: we need to notify the user so that the client can
300
- // fetch new messages. The client can't fetch messages
301
- // immediately because messages may not have been saved yet.
302
- notify(userId, persistedThread.agentId, {
303
- type: "thread:messages-updated",
304
- payload: {
305
- threadId: thread.id,
306
- thread: toThreadSummary(persistedThread, false),
307
- },
308
- });
353
+ return trimMessagesToFit(contextMessages, maxContextTokens - actualFixedCost);
354
+ }
309
355
 
310
- setTimeout(() => removeStream(thread.id, activeStream.id), 10_000);
356
+ const TITLE_SYSTEM_PROMPT = `You are an expert in generating titles for threads of chats
357
+ given the first message in that thread.
311
358
 
312
- // Fire-and-forget: optimize context for next request
313
- // Runs after response is delivered no perceived latency
314
- try {
315
- optimizeThreadContext(thread, finishedMessages, config);
316
- } catch (err) {
317
- console.error("[cortex-server] Context optimization failed:", err);
318
- }
319
- },
320
- });
321
- }
359
+ When asked, only respond with the title without saying you're
360
+ going to do so or any other speech. Spit out only the title.`;
322
361
 
323
362
  export async function generateTitle(
324
363
  threadId: string,
@@ -326,53 +365,44 @@ export async function generateTitle(
326
365
  userId: string,
327
366
  config: ResolvedCortexAgentConfig,
328
367
  ) {
329
- const model = createModel(config.model);
330
-
331
- const { output } = await generateText({
332
- model,
333
- system: `You are an expert in generating titles for threads of chats
334
- given the first message in that thread.
335
-
336
- When asked, only respond with the title without saying you're
337
- going to do so or any other speech. Spit out only the title.`,
338
- prompt: `Generate a title for this prompt: ${prompt}`,
368
+ const title = await chat({
369
+ adapter: createModel(config.model, { name: "title" }),
370
+ stream: false,
371
+ systemPrompts: [TITLE_SYSTEM_PROMPT],
372
+ messages: [
373
+ {
374
+ id: generateMessageId(),
375
+ role: "user",
376
+ parts: [{ type: "text", content: `Generate a title for this prompt: ${prompt}` }],
377
+ },
378
+ ],
339
379
  });
340
380
 
341
- await config.db.threads.updateTitle(threadId, output ?? "");
381
+ // A failed generation resolves to empty text rather than throwing, and the
382
+ // only trigger for a retry is the title still being null — so writing "" here
383
+ // would leave the thread permanently unnamed.
384
+ if (!title) return;
385
+
386
+ await config.db.threads.updateTitle(threadId, title);
342
387
 
343
388
  const thread = await config.db.threads.getById(userId, threadId);
344
389
  if (!thread) return;
345
390
 
346
391
  notify(userId, thread.agentId, {
347
392
  type: "thread:title-updated",
348
- payload: { thread: toThreadSummary(thread, isStreamRunning(thread.id)) },
393
+ payload: { thread: toThreadSummary(thread, await isRunning(thread.id)) },
349
394
  });
350
395
  }
351
396
 
352
- const TERMINAL_TOOL_STATES = new Set(["output-available", "output-error", "output-denied"]);
353
-
354
- function finalizeAbortedMessages(messages: UIMessage[]) {
355
- const lastMessage = messages.at(-1);
356
- if (!lastMessage || lastMessage.role !== "assistant") return;
357
-
358
- lastMessage.parts = lastMessage.parts.map((part) => {
359
- if ((part.type === "text" || part.type === "reasoning") && part.state === "streaming") {
360
- return { ...part, state: "done" as const };
361
- }
362
-
363
- if ("toolCallId" in part && "state" in part) {
364
- const toolState = part.state;
365
- if (!TERMINAL_TOOL_STATES.has(toolState)) {
366
- const { approval: _, ...rest } = part;
367
- return {
368
- ...rest,
369
- state: "output-error" as const,
370
- errorText: "Generation was aborted",
371
- output: undefined,
372
- };
373
- }
374
- }
375
-
376
- return part;
397
+ function emitToolProgress(
398
+ userId: string,
399
+ thread: Thread,
400
+ toolName: string,
401
+ toolCallId: string,
402
+ status: "started" | "finished",
403
+ ) {
404
+ notify(userId, thread.agentId, {
405
+ type: "thread:tool-progress",
406
+ payload: { threadId: thread.id, toolCallId, toolName, status },
377
407
  });
378
408
  }