@m6d/cortex-server 1.8.0 → 2.0.1

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 (354) 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/rich-text.ts +207 -0
  10. package/contracts/runtime.ts +243 -0
  11. package/contracts/wire.ts +143 -0
  12. package/dist/contracts/graph/embed.d.ts +22 -0
  13. package/dist/{src/lib → contracts}/graph/helpers.d.ts +16 -14
  14. package/dist/{src/lib → contracts}/graph/neo4j.d.ts +5 -5
  15. package/dist/contracts/graph/schema.d.ts +62 -0
  16. package/dist/{src/lib → contracts}/graph/types.d.ts +1 -1
  17. package/dist/contracts/graph.d.ts +14 -0
  18. package/dist/contracts/rich-text.d.ts +32 -0
  19. package/dist/contracts/runtime.d.ts +310 -0
  20. package/dist/contracts/wire.d.ts +120 -0
  21. package/dist/src/lib/adapters/database/index.d.ts +61 -0
  22. package/dist/src/lib/adapters/database/message-content.d.ts +30 -0
  23. package/dist/src/lib/adapters/database/mssql/attachments.d.ts +100 -0
  24. package/dist/src/lib/adapters/database/mssql/client.d.ts +8 -0
  25. package/dist/src/lib/adapters/database/mssql/index.d.ts +194 -0
  26. package/dist/src/lib/adapters/database/mssql/llm-requests.d.ts +17 -0
  27. package/dist/src/lib/adapters/database/mssql/messages.d.ts +37 -0
  28. package/dist/src/lib/adapters/database/mssql/threads.d.ts +50 -0
  29. package/dist/src/lib/adapters/database/postgres/attachments.d.ts +113 -0
  30. package/dist/src/lib/adapters/database/postgres/client.d.ts +8 -0
  31. package/dist/src/lib/adapters/database/postgres/index.d.ts +207 -0
  32. package/dist/src/lib/adapters/database/postgres/llm-requests.d.ts +17 -0
  33. package/dist/src/lib/adapters/database/postgres/messages.d.ts +37 -0
  34. package/dist/src/lib/adapters/database/postgres/threads.d.ts +50 -0
  35. package/{src/lib/adapters/storage.ts → dist/src/lib/adapters/storage/index.d.ts} +1 -1
  36. package/dist/src/lib/adapters/{minio.d.ts → storage/minio.d.ts} +1 -1
  37. package/dist/src/lib/ai/active-runs.d.ts +63 -0
  38. package/dist/src/lib/ai/attachments.d.ts +30 -0
  39. package/dist/src/lib/ai/backend-url.d.ts +16 -0
  40. package/dist/src/lib/ai/cc-runtime.d.ts +61 -0
  41. package/dist/src/lib/ai/commit-gate.d.ts +21 -0
  42. package/dist/src/lib/ai/context/builder.d.ts +6 -7
  43. package/dist/src/lib/ai/context/compression-middleware.d.ts +15 -0
  44. package/dist/src/lib/ai/context/compressor.d.ts +2 -13
  45. package/dist/src/lib/ai/context/intra-turn-compressor.d.ts +9 -9
  46. package/dist/src/lib/ai/context/optimize-thread-context.d.ts +9 -0
  47. package/dist/src/lib/ai/context/summarizer.d.ts +3 -3
  48. package/dist/src/lib/ai/context/token-estimator.d.ts +5 -8
  49. package/dist/src/lib/ai/fetch.d.ts +1 -1
  50. package/dist/src/lib/ai/finish-turn.d.ts +23 -0
  51. package/dist/src/lib/ai/helpers.d.ts +37 -4
  52. package/dist/src/lib/ai/index.d.ts +6 -3
  53. package/dist/src/lib/ai/inspector.d.ts +41 -0
  54. package/dist/src/lib/ai/interceptors/request-interceptor.d.ts +6 -5
  55. package/dist/src/lib/ai/prompt.d.ts +5 -4
  56. package/dist/src/lib/ai/redis-runs.d.ts +41 -0
  57. package/dist/src/lib/ai/redis-stream-log.d.ts +39 -0
  58. package/dist/src/lib/ai/tools/execute-code.tool.d.ts +18 -4
  59. package/dist/src/lib/ai/tools/query-graph.tool.d.ts +13 -5
  60. package/dist/src/lib/ai/tools/read-attachment.tool.d.ts +21 -0
  61. package/dist/src/lib/ai/tools/search-common.d.ts +17 -0
  62. package/dist/src/lib/ai/tools/search-knowledge.tool.d.ts +15 -0
  63. package/dist/src/lib/ai/tools/search-services.tool.d.ts +12 -0
  64. package/dist/src/lib/ai/tools/search-tools.tool.d.ts +12 -0
  65. package/dist/src/lib/ai/turn-tools.d.ts +39 -0
  66. package/dist/src/lib/ai/vision/rasterize-pdf.d.ts +26 -0
  67. package/dist/src/lib/ai/vision/rasterize-pdf.worker.d.ts +1 -0
  68. package/dist/src/lib/ai/vision/vision-reader.d.ts +45 -0
  69. package/dist/src/lib/cc/client.d.ts +183 -0
  70. package/dist/src/lib/cc/config-cache.d.ts +20 -0
  71. package/dist/src/lib/cc/format.d.ts +23 -0
  72. package/dist/src/lib/cc/registry.d.ts +34 -0
  73. package/dist/src/lib/cc/types.d.ts +27 -0
  74. package/dist/src/lib/config.d.ts +81 -64
  75. package/dist/src/lib/db/drizzle.config.mssql.d.ts +2 -0
  76. package/dist/src/lib/db/drizzle.config.pg.d.ts +2 -0
  77. package/dist/src/lib/db/migrate.d.ts +7 -1
  78. package/dist/src/lib/db/{schema.d.ts → schema.mssql.d.ts} +138 -29
  79. package/dist/src/lib/db/schema.pg.d.ts +546 -0
  80. package/dist/src/lib/factory.d.ts +2 -12
  81. package/dist/src/lib/graph/index.d.ts +10 -11
  82. package/dist/src/lib/graph/resolver.d.ts +2 -3
  83. package/dist/src/lib/index.d.ts +3 -3
  84. package/dist/src/lib/redis.d.ts +19 -0
  85. package/dist/src/lib/routes/chat.d.ts +1 -1
  86. package/dist/src/lib/routes/files.d.ts +1 -1
  87. package/dist/src/lib/routes/owned-thread.d.ts +17 -0
  88. package/dist/src/lib/routes/threads.d.ts +1 -1
  89. package/dist/src/lib/routes/ws.d.ts +1 -1
  90. package/dist/src/lib/types.d.ts +20 -37
  91. package/dist/src/lib/ws/connections.d.ts +2 -1
  92. package/package.json +33 -18
  93. package/src/lib/adapters/database/index.ts +78 -0
  94. package/src/lib/adapters/database/message-content.ts +39 -0
  95. package/src/lib/adapters/database/mssql/attachments.ts +90 -0
  96. package/src/lib/adapters/database/mssql/client.ts +11 -0
  97. package/src/lib/adapters/database/mssql/index.ts +23 -0
  98. package/src/lib/adapters/database/mssql/llm-requests.ts +43 -0
  99. package/src/lib/adapters/database/mssql/messages.ts +92 -0
  100. package/src/lib/adapters/database/mssql/threads.ts +83 -0
  101. package/src/lib/adapters/database/postgres/attachments.ts +88 -0
  102. package/src/lib/adapters/database/postgres/client.ts +11 -0
  103. package/src/lib/adapters/database/postgres/index.ts +23 -0
  104. package/src/lib/adapters/database/postgres/llm-requests.ts +45 -0
  105. package/src/lib/adapters/database/postgres/messages.ts +92 -0
  106. package/src/lib/adapters/database/postgres/threads.ts +84 -0
  107. package/{dist/src/lib/adapters/storage.d.ts → src/lib/adapters/storage/index.ts} +1 -1
  108. package/src/lib/adapters/{minio.ts → storage/minio.ts} +5 -5
  109. package/src/lib/ai/active-runs.ts +157 -0
  110. package/src/lib/ai/attachments.ts +144 -0
  111. package/src/lib/ai/backend-url.ts +27 -0
  112. package/src/lib/ai/cc-runtime.ts +63 -0
  113. package/src/lib/ai/commit-gate.ts +116 -0
  114. package/src/lib/ai/context/builder.ts +12 -14
  115. package/src/lib/ai/context/compression-middleware.ts +44 -0
  116. package/src/lib/ai/context/compressor.ts +67 -59
  117. package/src/lib/ai/context/intra-turn-compressor.ts +121 -204
  118. package/src/lib/ai/context/optimize-thread-context.ts +50 -0
  119. package/src/lib/ai/context/summarizer.ts +28 -30
  120. package/src/lib/ai/context/token-estimator.ts +35 -30
  121. package/src/lib/ai/fetch.ts +10 -5
  122. package/src/lib/ai/finish-turn.ts +75 -0
  123. package/src/lib/ai/helpers.ts +83 -15
  124. package/src/lib/ai/index.ts +366 -329
  125. package/src/lib/ai/inspector.ts +112 -0
  126. package/src/lib/ai/interceptors/request-interceptor.ts +70 -42
  127. package/src/lib/ai/prompt.ts +126 -125
  128. package/src/lib/ai/redis-runs.ts +167 -0
  129. package/src/lib/ai/redis-stream-log.ts +219 -0
  130. package/src/lib/ai/tools/execute-code.tool.ts +178 -67
  131. package/src/lib/ai/tools/query-graph.tool.ts +7 -8
  132. package/src/lib/ai/tools/read-attachment.tool.ts +59 -0
  133. package/src/lib/ai/tools/search-common.ts +15 -0
  134. package/src/lib/ai/tools/search-knowledge.tool.ts +33 -0
  135. package/src/lib/ai/tools/search-services.tool.ts +31 -0
  136. package/src/lib/ai/tools/search-tools.tool.ts +29 -0
  137. package/src/lib/ai/turn-tools.ts +154 -0
  138. package/src/lib/ai/vision/rasterize-pdf.ts +69 -0
  139. package/src/lib/ai/vision/rasterize-pdf.worker.ts +81 -0
  140. package/src/lib/ai/vision/vision-reader.ts +130 -0
  141. package/src/lib/auth/middleware.ts +5 -3
  142. package/src/lib/cc/client.ts +273 -0
  143. package/src/lib/cc/config-cache.ts +32 -0
  144. package/src/lib/cc/format.ts +92 -0
  145. package/src/lib/cc/registry.ts +62 -0
  146. package/src/lib/cc/types.ts +44 -0
  147. package/src/lib/config.ts +102 -71
  148. package/src/lib/db/drizzle.config.mssql.ts +9 -0
  149. package/src/lib/db/drizzle.config.pg.ts +9 -0
  150. package/src/lib/db/migrate.ts +45 -10
  151. package/src/lib/db/migrations/mssql/20260729092726_talented_wilson_fisk/migration.sql +20 -0
  152. package/src/lib/db/migrations/mssql/20260729092726_talented_wilson_fisk/snapshot.json +497 -0
  153. package/src/lib/db/migrations/mssql/20260801003652_perpetual_blue_shield/migration.sql +1 -0
  154. package/src/lib/db/migrations/mssql/20260801003652_perpetual_blue_shield/snapshot.json +516 -0
  155. package/src/lib/db/migrations/mssql/20260812174642_user_id_text/migration.sql +2 -0
  156. package/src/lib/db/migrations/mssql/20260812174642_user_id_text/snapshot.json +516 -0
  157. package/src/lib/db/migrations/pg/20260801084117_thick_ben_grimm/migration.sql +53 -0
  158. package/src/lib/db/migrations/pg/20260801084117_thick_ben_grimm/snapshot.json +584 -0
  159. package/src/lib/db/migrations/pg/20260812174622_user_id_text/migration.sql +2 -0
  160. package/src/lib/db/migrations/pg/20260812174622_user_id_text/snapshot.json +584 -0
  161. package/src/lib/db/{schema.ts → schema.mssql.ts} +29 -18
  162. package/src/lib/db/schema.pg.ts +95 -0
  163. package/src/lib/factory.ts +44 -89
  164. package/src/lib/graph/index.ts +12 -40
  165. package/src/lib/graph/resolver.ts +146 -116
  166. package/src/lib/index.ts +11 -4
  167. package/src/lib/redis.ts +44 -0
  168. package/src/lib/routes/chat.ts +91 -62
  169. package/src/lib/routes/files.ts +105 -83
  170. package/src/lib/routes/owned-thread.ts +17 -0
  171. package/src/lib/routes/threads.ts +14 -30
  172. package/src/lib/routes/ws.ts +9 -3
  173. package/src/lib/types.ts +37 -36
  174. package/src/lib/ws/connections.ts +9 -1
  175. package/tsconfig.json +48 -0
  176. package/dist/src/lib/adapters/database.d.ts +0 -45
  177. package/dist/src/lib/adapters/mssql.d.ts +0 -3
  178. package/dist/src/lib/ai/active-streams.d.ts +0 -14
  179. package/dist/src/lib/ai/context/index.d.ts +0 -16
  180. package/dist/src/lib/ai/tools/capture-files.tool.d.ts +0 -6
  181. package/dist/src/lib/cli/extract-endpoints.d.ts +0 -6
  182. package/dist/src/lib/graph/expand-domains.d.ts +0 -2
  183. package/dist/src/lib/graph/generate-cypher.d.ts +0 -22
  184. package/dist/src/lib/graph/seed.d.ts +0 -19
  185. package/dist/src/lib/graph/validate.d.ts +0 -2
  186. package/dist/src/lib/routes/index.d.ts +0 -4
  187. package/dist/src/lib/ws/events.d.ts +0 -39
  188. package/dist/src/lib/ws/index.d.ts +0 -3
  189. package/dist/src/lib/ws/notify.d.ts +0 -2
  190. package/src/lib/adapters/database.ts +0 -48
  191. package/src/lib/adapters/mssql.ts +0 -231
  192. package/src/lib/ai/active-streams.ts +0 -123
  193. package/src/lib/ai/context/index.ts +0 -80
  194. package/src/lib/ai/tools/capture-files.tool.ts +0 -20
  195. package/src/lib/cli/extract-endpoints.ts +0 -550
  196. package/src/lib/graph/expand-domains.ts +0 -276
  197. package/src/lib/graph/generate-cypher.ts +0 -192
  198. package/src/lib/graph/seed.ts +0 -162
  199. package/src/lib/graph/validate.ts +0 -78
  200. package/src/lib/routes/index.ts +0 -4
  201. package/src/lib/ws/events.ts +0 -39
  202. package/src/lib/ws/index.ts +0 -11
  203. package/src/lib/ws/notify.ts +0 -9
  204. package/src/sample/db/client.ts +0 -9
  205. package/src/sample/db/migrations/20260411023125_rich_purple_man/migration.sql +0 -5
  206. package/src/sample/db/migrations/20260411023125_rich_purple_man/snapshot.json +0 -50
  207. package/src/sample/db/schema.ts +0 -10
  208. package/src/sample/domains/account/concepts/session.concept.ts +0 -17
  209. package/src/sample/domains/account/endpoints/listCurrentSessions.endpoint.ts +0 -31
  210. package/src/sample/domains/account/endpoints/revokeAllOtherSessions.endpoint.ts +0 -22
  211. package/src/sample/domains/account/endpoints/revokeCurrentSession.endpoint.ts +0 -33
  212. package/src/sample/domains/account/index.ts +0 -18
  213. package/src/sample/domains/appraisals/concepts/appraisalCycle.concept.ts +0 -15
  214. package/src/sample/domains/appraisals/concepts/appraisalRecord.concept.ts +0 -20
  215. package/src/sample/domains/appraisals/concepts/appraisalRecordCompetency.concept.ts +0 -10
  216. package/src/sample/domains/appraisals/concepts/appraisalRecordFunctionalRequirement.concept.ts +0 -10
  217. package/src/sample/domains/appraisals/concepts/appraisalRecordGoal.concept.ts +0 -17
  218. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordById.endpoint.ts +0 -321
  219. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordCompetencies.endpoint.ts +0 -120
  220. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordFunctionalRequirements.endpoint.ts +0 -126
  221. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordGoals.endpoint.ts +0 -104
  222. package/src/sample/domains/appraisals/endpoints/getAppraisalRecordScore.endpoint.ts +0 -58
  223. package/src/sample/domains/appraisals/endpoints/listAppraisalCyclesSimple.endpoint.ts +0 -52
  224. package/src/sample/domains/appraisals/endpoints/listAppraisalRecords.endpoint.ts +0 -245
  225. package/src/sample/domains/appraisals/endpoints/listAppraisalTemplatesSimple.endpoint.ts +0 -52
  226. package/src/sample/domains/appraisals/index.ts +0 -40
  227. package/src/sample/domains/appraisals/rules/appraisalRecordScoping.rule.ts +0 -7
  228. package/src/sample/domains/attendance/concepts/attendancePermission.concept.ts +0 -17
  229. package/src/sample/domains/attendance/concepts/attendanceTransaction.concept.ts +0 -17
  230. package/src/sample/domains/attendance/concepts/publicHoliday.concept.ts +0 -10
  231. package/src/sample/domains/attendance/concepts/punch.concept.ts +0 -19
  232. package/src/sample/domains/attendance/endpoints/getAttendanceTransactionById.endpoint.ts +0 -444
  233. package/src/sample/domains/attendance/endpoints/getCurrentAttendanceProfile.endpoint.ts +0 -886
  234. package/src/sample/domains/attendance/endpoints/getCurrentAttendanceStatistics.endpoint.ts +0 -49
  235. package/src/sample/domains/attendance/endpoints/listAttendancePermissions.endpoint.ts +0 -176
  236. package/src/sample/domains/attendance/endpoints/listAttendanceTransactionStates.endpoint.ts +0 -26
  237. package/src/sample/domains/attendance/endpoints/listAttendanceTransactions.endpoint.ts +0 -373
  238. package/src/sample/domains/attendance/endpoints/listPublicHolidaysSimple.endpoint.ts +0 -52
  239. package/src/sample/domains/attendance/endpoints/listPunchTypes.endpoint.ts +0 -25
  240. package/src/sample/domains/attendance/endpoints/listPunches.endpoint.ts +0 -178
  241. package/src/sample/domains/attendance/index.ts +0 -41
  242. package/src/sample/domains/attendance/services/attendancePermissions.service.ts +0 -15
  243. package/src/sample/domains/employees/concepts/employee.concept.ts +0 -20
  244. package/src/sample/domains/employees/concepts/employeeBankAccount.concept.ts +0 -19
  245. package/src/sample/domains/employees/concepts/employeeChild.concept.ts +0 -19
  246. package/src/sample/domains/employees/concepts/employeeDocument.concept.ts +0 -21
  247. package/src/sample/domains/employees/concepts/employeeEvent.concept.ts +0 -21
  248. package/src/sample/domains/employees/concepts/employeeExperience.concept.ts +0 -18
  249. package/src/sample/domains/employees/concepts/employeeJobLevelChange.concept.ts +0 -21
  250. package/src/sample/domains/employees/concepts/employeeMedal.concept.ts +0 -20
  251. package/src/sample/domains/employees/concepts/employeeQualification.concept.ts +0 -20
  252. package/src/sample/domains/employees/concepts/employeeSpouse.concept.ts +0 -21
  253. package/src/sample/domains/employees/concepts/employeeViolation.concept.ts +0 -19
  254. package/src/sample/domains/employees/endpoints/getCurrentEmployee.endpoint.ts +0 -1651
  255. package/src/sample/domains/employees/endpoints/getCurrentIdentity.endpoint.ts +0 -197
  256. package/src/sample/domains/employees/endpoints/getCurrentManagers.endpoint.ts +0 -142
  257. package/src/sample/domains/employees/endpoints/getEmployeeById.endpoint.ts +0 -1659
  258. package/src/sample/domains/employees/endpoints/getEmployeeStatistics.endpoint.ts +0 -43
  259. package/src/sample/domains/employees/endpoints/listBankAccounts.endpoint.ts +0 -223
  260. package/src/sample/domains/employees/endpoints/listChildren.endpoint.ts +0 -211
  261. package/src/sample/domains/employees/endpoints/listDocuments.endpoint.ts +0 -222
  262. package/src/sample/domains/employees/endpoints/listEmployeesSimple.endpoint.ts +0 -156
  263. package/src/sample/domains/employees/endpoints/listEvents.endpoint.ts +0 -120
  264. package/src/sample/domains/employees/endpoints/listExperiences.endpoint.ts +0 -188
  265. package/src/sample/domains/employees/endpoints/listJobLevelChanges.endpoint.ts +0 -240
  266. package/src/sample/domains/employees/endpoints/listMedals.endpoint.ts +0 -266
  267. package/src/sample/domains/employees/endpoints/listQualifications.endpoint.ts +0 -265
  268. package/src/sample/domains/employees/endpoints/listSpouses.endpoint.ts +0 -278
  269. package/src/sample/domains/employees/endpoints/listViolations.endpoint.ts +0 -245
  270. package/src/sample/domains/employees/index.ts +0 -75
  271. package/src/sample/domains/employees/services/employees.service.ts +0 -181
  272. package/src/sample/domains/index.ts +0 -10
  273. package/src/sample/domains/leaves/concepts/leave.concept.ts +0 -21
  274. package/src/sample/domains/leaves/concepts/leaveBalance.concept.ts +0 -17
  275. package/src/sample/domains/leaves/concepts/leaveBalanceTransaction.concept.ts +0 -16
  276. package/src/sample/domains/leaves/concepts/leaveType.concept.ts +0 -13
  277. package/src/sample/domains/leaves/concepts/leaveTypes.concept.ts +0 -205
  278. package/src/sample/domains/leaves/endpoints/getBalance.endpoint.ts +0 -53
  279. package/src/sample/domains/leaves/endpoints/getLeaveById.endpoint.ts +0 -53
  280. package/src/sample/domains/leaves/endpoints/listBalanceTransactions.endpoint.ts +0 -196
  281. package/src/sample/domains/leaves/endpoints/listBalanceTypes.endpoint.ts +0 -26
  282. package/src/sample/domains/leaves/endpoints/listBalances.endpoint.ts +0 -201
  283. package/src/sample/domains/leaves/endpoints/listLeaveTypes.endpoint.ts +0 -28
  284. package/src/sample/domains/leaves/endpoints/listLeaves.endpoint.ts +0 -234
  285. package/src/sample/domains/leaves/index.ts +0 -38
  286. package/src/sample/domains/leaves/services/leaveRequests.service.ts +0 -232
  287. package/src/sample/domains/notifications/concepts/notification.concept.ts +0 -19
  288. package/src/sample/domains/notifications/endpoints/countMyNotifications.endpoint.ts +0 -21
  289. package/src/sample/domains/notifications/endpoints/getMyNotification.endpoint.ts +0 -57
  290. package/src/sample/domains/notifications/endpoints/listMyNotifications.endpoint.ts +0 -59
  291. package/src/sample/domains/notifications/endpoints/markMyNotificationAsRead.endpoint.ts +0 -32
  292. package/src/sample/domains/notifications/index.ts +0 -20
  293. package/src/sample/domains/payroll/concepts/payslip.concept.ts +0 -18
  294. package/src/sample/domains/payroll/concepts/salaryCertificate.concept.ts +0 -17
  295. package/src/sample/domains/payroll/concepts/salaryCertificateRequestingEntity.concept.ts +0 -8
  296. package/src/sample/domains/payroll/endpoints/getPayslipById.endpoint.ts +0 -238
  297. package/src/sample/domains/payroll/endpoints/listPayslips.endpoint.ts +0 -216
  298. package/src/sample/domains/payroll/endpoints/listSalaryCertificateLanguages.endpoint.ts +0 -25
  299. package/src/sample/domains/payroll/endpoints/listSalaryCertificateRequestingEntities.endpoint.ts +0 -62
  300. package/src/sample/domains/payroll/endpoints/listSalaryCertificateTypes.endpoint.ts +0 -24
  301. package/src/sample/domains/payroll/endpoints/listSalaryCertificates.endpoint.ts +0 -122
  302. package/src/sample/domains/payroll/endpoints/previewSalaryCertificate.endpoint.ts +0 -45
  303. package/src/sample/domains/payroll/index.ts +0 -32
  304. package/src/sample/domains/payroll/rules/payrollEmployeeContext.rule.ts +0 -7
  305. package/src/sample/domains/payroll/services/salaryCertificates.service.ts +0 -15
  306. package/src/sample/domains/servicing/concepts/service.concept.ts +0 -6
  307. package/src/sample/domains/servicing/concepts/serviceRequest.concept.ts +0 -12
  308. package/src/sample/domains/servicing/concepts/serviceRequestApproval.concept.ts +0 -9
  309. package/src/sample/domains/servicing/endpoints/cancelServiceRequest.endpoint.ts +0 -21
  310. package/src/sample/domains/servicing/endpoints/createServiceRequest.endpoint.ts +0 -511
  311. package/src/sample/domains/servicing/endpoints/getServiceRequest.endpoint.ts +0 -352
  312. package/src/sample/domains/servicing/endpoints/getServiceRequestFormItems.endpoint.ts +0 -219
  313. package/src/sample/domains/servicing/endpoints/getServiceRequestTransactionFormItems.endpoint.ts +0 -216
  314. package/src/sample/domains/servicing/endpoints/getServiceStates.endpoint.ts +0 -47
  315. package/src/sample/domains/servicing/endpoints/invokeServiceRequestAction.endpoint.ts +0 -139
  316. package/src/sample/domains/servicing/endpoints/listServiceRequests.endpoint.ts +0 -292
  317. package/src/sample/domains/servicing/endpoints/listServices.endpoint.ts +0 -108
  318. package/src/sample/domains/servicing/endpoints/updateServiceRequest.endpoint.ts +0 -355
  319. package/src/sample/domains/servicing/index.ts +0 -44
  320. package/src/sample/domains/servicing/rules/checkValidActions.rule.ts +0 -7
  321. package/src/sample/domains/servicing/rules/createServiceRequest.rule.ts +0 -7
  322. package/src/sample/domains/servicing/rules/filterServiceRequestsByState.rule.ts +0 -7
  323. package/src/sample/domains/servicing/rules/passingOptionFields.rule.ts +0 -7
  324. package/src/sample/domains/shared/rules/pagination.rule.ts +0 -7
  325. package/src/sample/domains/suggestions/concepts/suggestion.concept.ts +0 -19
  326. package/src/sample/domains/suggestions/concepts/suggestionCategory.concept.ts +0 -7
  327. package/src/sample/domains/suggestions/concepts/suggestionImpactLevel.concept.ts +0 -7
  328. package/src/sample/domains/suggestions/endpoints/createSuggestion.endpoint.ts +0 -705
  329. package/src/sample/domains/suggestions/endpoints/deleteSuggestion.endpoint.ts +0 -32
  330. package/src/sample/domains/suggestions/endpoints/getSuggestionById.endpoint.ts +0 -635
  331. package/src/sample/domains/suggestions/endpoints/listSuggestionCategories.endpoint.ts +0 -62
  332. package/src/sample/domains/suggestions/endpoints/listSuggestionImpactLevels.endpoint.ts +0 -62
  333. package/src/sample/domains/suggestions/endpoints/listSuggestionStates.endpoint.ts +0 -24
  334. package/src/sample/domains/suggestions/endpoints/listSuggestions.endpoint.ts +0 -132
  335. package/src/sample/domains/suggestions/endpoints/updateSuggestion.endpoint.ts +0 -690
  336. package/src/sample/domains/suggestions/endpoints/updateSuggestionState.endpoint.ts +0 -39
  337. package/src/sample/domains/suggestions/index.ts +0 -34
  338. package/src/sample/domains/suggestions/rules/suggestionScope.rule.ts +0 -7
  339. package/src/sample/domains/surveys/concepts/survey.concept.ts +0 -10
  340. package/src/sample/domains/surveys/concepts/surveyResponse.concept.ts +0 -14
  341. package/src/sample/domains/surveys/endpoints/getSurveyResponseById.endpoint.ts +0 -131
  342. package/src/sample/domains/surveys/endpoints/getTargetedSurvey.endpoint.ts +0 -129
  343. package/src/sample/domains/surveys/endpoints/listTargetedSurveys.endpoint.ts +0 -160
  344. package/src/sample/domains/surveys/endpoints/respondToSurvey.endpoint.ts +0 -144
  345. package/src/sample/domains/surveys/index.ts +0 -21
  346. package/src/sample/drizzle.config.ts +0 -11
  347. package/src/sample/index.bak.ts +0 -341
  348. package/src/sample/index.ts +0 -318
  349. package/src/sample/official-statements/defaults.ts +0 -57
  350. package/src/sample/official-statements/routes.ts +0 -137
  351. package/src/sample/official-statements/service.ts +0 -148
  352. package/src/sample/official-statements/types.ts +0 -23
  353. /package/src/lib/db/migrations/{20260326231647_nice_speedball → mssql/20260326231647_nice_speedball}/migration.sql +0 -0
  354. /package/src/lib/db/migrations/{20260326231647_nice_speedball → mssql/20260326231647_nice_speedball}/snapshot.json +0 -0
@@ -1,324 +1,370 @@
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
+ // Computed per request; consumer-supplied context
194
+ // with the same key wins via the spreads below. No
195
+ // such default for `timezone`: the server's zone is
196
+ // not the session's, so guessing would feed the
197
+ // model wrong user context — only the consumer can
198
+ // supply it.
199
+ utcTime: new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
200
+ ...session,
201
+ ...requestContext,
202
+ locale: cc.locale,
203
+ }),
204
+ }
205
+ : undefined,
206
+ buildAttachmentListing(threadAttachments, lastUserMessage?.id, {
207
+ readAttachment: tools.some((tool) => tool.name === "readAttachment"),
208
+ attachmentSentinels: hasDefaultAttachmentInterceptor(config),
209
+ }),
210
+ );
211
+
212
+ const stream = chat({
213
+ adapter: model,
214
+ // Client tool declarations ride in on every request, so the server
215
+ // never re-declares them.
216
+ tools: mergeAgentTools(tools, params.tools),
217
+ messages: convertMessagesToModelMessages(
218
+ fitToContextWindow(contextMessages, systemPrompt, tools, config),
219
+ ),
220
+ systemPrompts: [systemPrompt],
221
+ agentLoopStrategy: maxIterations(MAX_ITERATIONS),
222
+ abortController: run.abortController,
223
+ middleware: [
224
+ createCompressionMiddleware(config),
225
+ ...(instrumentation ? [instrumentation] : []),
226
+ inspector.middleware,
227
+ ],
228
+ threadId: thread.id,
229
+ runId: run.runId,
230
+ });
231
+
232
+ const producer = toServerSentEventsResponse(
233
+ commitBeforeTerminal(stream, {
234
+ isAborted: () => run.abortController.signal.aborted,
235
+ commit: async function (produced) {
236
+ const messages = produced.map(toChatMessage).map(withTurnMetadata);
237
+ await finishTurn({
238
+ messages,
239
+ // Summarization and the consumer hook judge the thread, not
240
+ // the turn — the run's own output is only its tail.
241
+ threadMessages: [...allMessages, ...messages],
242
+ isAborted: run.abortController.signal.aborted,
243
+ assistantMessageId: messages.at(-1)?.id,
244
+ thread,
245
+ userId,
246
+ capturedSteps: inspector.steps,
247
+ config,
248
+ });
249
+ },
250
+ }),
251
+ {
252
+ // Append per chunk rather than per batch of 32: a client joining
253
+ // the log is waiting on real tokens, not on a buffer filling up.
254
+ durability: { adapter: producerLog(run.runId), batch: 1 },
255
+ abortController: run.abortController,
256
+ },
257
+ );
258
+
259
+ function withTurnMetadata(message: ChatMessage, index: number, all: ChatMessage[]) {
260
+ if (index !== all.length - 1 || message.role !== "assistant") return message;
261
+
262
+ return {
263
+ ...message,
264
+ metadata: {
265
+ modelId: inspector.modelId,
266
+ isAborted: run.abortController.signal.aborted,
267
+ tokenUsage: inspector.tokenUsage,
268
+ } satisfies MessageMetadata,
269
+ };
270
+ }
99
271
 
100
- if (neo4j) {
101
- builtInTools["queryGraph"] = createQueryGraphTool(neo4j);
272
+ // Generation outlives the request that started it, so a reload mid-turn
273
+ // rejoins the log instead of restarting the model. Draining into a sink
274
+ // that never cancels is what detaches the producer from this response;
275
+ // the client gets a reader on the same log.
276
+ void producer
277
+ .body!.pipeTo(new WritableStream())
278
+ .catch(() => undefined)
279
+ .finally(() => releaseRun(thread.id, run.runId));
280
+
281
+ return resumeServerSentEventsResponse({ adapter: joinLog(thread.id, run.runId) });
282
+ } catch (error) {
283
+ releaseRun(thread.id, run.runId);
284
+ throw error;
102
285
  }
286
+ }
103
287
 
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
- };
288
+ /** Releasing the claim must never mask the turn's own error or reject unhandled. */
289
+ function releaseRun(threadId: string, runId: string) {
290
+ endRun(threadId, runId).catch((error: unknown) =>
291
+ console.error("[cortex-server] Failed to release run claim:", error),
292
+ );
293
+ }
115
294
 
116
- builtInTools["executeCode"] = createExecuteCodeTool(backendFetchWithInterceptor, token);
117
- }
295
+ /** Drops the SDK's transport shape into the envelope cortex persists and serves. */
296
+ function toChatMessage(message: UIMessage) {
297
+ return { id: message.id, role: message.role, parts: message.parts } satisfies ChatMessage;
298
+ }
118
299
 
119
- const resolvedUserTools =
120
- typeof config.tools === "function"
121
- ? config.tools({ thread, userId, token, session, requestContext })
122
- : config.tools;
300
+ /**
301
+ * An assistant turn the client is sending back because it resolved a tool call.
302
+ *
303
+ * The wire fans that turn out into a `role: "tool"` message per result, and each
304
+ * of those normalizes back into an assistant message carrying only the result,
305
+ * under a freshly generated id. Stored as rows of their own they would replay as
306
+ * assistant turns answering calls that no message makes, so the turn is picked
307
+ * out by holding both halves of the exchange.
308
+ */
309
+ function answersToolCalls(message: ChatMessage) {
310
+ return (
311
+ message.role === "assistant" &&
312
+ message.parts.some((part) => part.type === "tool-call") &&
313
+ message.parts.some((part) => part.type === "tool-result")
314
+ );
315
+ }
123
316
 
124
- const tools = {
125
- ...builtInTools,
126
- ...resolvedUserTools,
127
- } as ToolSet;
317
+ function textOf(message: ChatMessage | undefined) {
318
+ const part = message?.parts.find((candidate) => candidate.type === "text");
319
+ return part?.type === "text" ? part.content : "";
320
+ }
128
321
 
129
- const systemPrompt = await buildSystemPrompt(config, resolved, {
130
- session,
131
- requestContext,
132
- });
322
+ /**
323
+ * Builds the clients one turn needs. Neo4j runs vector search through an embedding
324
+ * model, so configuring a graph without one is a misconfiguration we surface
325
+ * loudly rather than quietly serving every turn with no graph context.
326
+ */
327
+ function createTurnProviders(config: ResolvedCortexAgentConfig, fetch: typeof globalThis.fetch) {
328
+ if (config.neo4j && !config.embedding) {
329
+ throw new HTTPException(500, {
330
+ message: "embedding config is required when neo4j is configured",
331
+ });
332
+ }
133
333
 
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.
334
+ const embed = config.embedding ? createEmbedder(config.embedding) : undefined;
335
+
336
+ return {
337
+ // The inspector's fetch is per-turn, so the model is too.
338
+ model: createModel(config.model, { fetch }),
339
+ embed,
340
+ neo4j: config.neo4j && embed ? createNeo4jClient(config.neo4j, embed) : undefined,
341
+ ccClient: config.controlCenter ? new ControlCenterClient(config.controlCenter) : null,
342
+ };
343
+ }
344
+
345
+ /**
346
+ * The context builder reserved a static token budget for the system prompt and
347
+ * tools before either existed. Now that both are real, drop the oldest messages
348
+ * if the reserve fell short.
349
+ */
350
+ function fitToContextWindow(
351
+ contextMessages: ChatMessage[],
352
+ systemPrompt: string,
353
+ tools: ToolSet,
354
+ config: ResolvedCortexAgentConfig,
355
+ ) {
137
356
  const actualFixedCost = estimateTokens(systemPrompt) + estimateTokens(JSON.stringify(tools));
138
357
  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
- });
358
+ if (actualFixedCost <= reservedTokenBudget) return contextMessages;
223
359
 
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
- });
360
+ return trimMessagesToFit(contextMessages, maxContextTokens - actualFixedCost);
361
+ }
309
362
 
310
- setTimeout(() => removeStream(thread.id, activeStream.id), 10_000);
363
+ const TITLE_SYSTEM_PROMPT = `You are an expert in generating titles for threads of chats
364
+ given the first message in that thread.
311
365
 
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
- }
366
+ When asked, only respond with the title without saying you're
367
+ going to do so or any other speech. Spit out only the title.`;
322
368
 
323
369
  export async function generateTitle(
324
370
  threadId: string,
@@ -326,53 +372,44 @@ export async function generateTitle(
326
372
  userId: string,
327
373
  config: ResolvedCortexAgentConfig,
328
374
  ) {
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}`,
375
+ const title = await chat({
376
+ adapter: createModel(config.model, { name: "title" }),
377
+ stream: false,
378
+ systemPrompts: [TITLE_SYSTEM_PROMPT],
379
+ messages: [
380
+ {
381
+ id: generateMessageId(),
382
+ role: "user",
383
+ parts: [{ type: "text", content: `Generate a title for this prompt: ${prompt}` }],
384
+ },
385
+ ],
339
386
  });
340
387
 
341
- await config.db.threads.updateTitle(threadId, output ?? "");
388
+ // A failed generation resolves to empty text rather than throwing, and the
389
+ // only trigger for a retry is the title still being null — so writing "" here
390
+ // would leave the thread permanently unnamed.
391
+ if (!title) return;
392
+
393
+ await config.db.threads.updateTitle(threadId, title);
342
394
 
343
395
  const thread = await config.db.threads.getById(userId, threadId);
344
396
  if (!thread) return;
345
397
 
346
398
  notify(userId, thread.agentId, {
347
399
  type: "thread:title-updated",
348
- payload: { thread: toThreadSummary(thread, isStreamRunning(thread.id)) },
400
+ payload: { thread: toThreadSummary(thread, await isRunning(thread.id)) },
349
401
  });
350
402
  }
351
403
 
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;
404
+ function emitToolProgress(
405
+ userId: string,
406
+ thread: Thread,
407
+ toolName: string,
408
+ toolCallId: string,
409
+ status: "started" | "finished",
410
+ ) {
411
+ notify(userId, thread.agentId, {
412
+ type: "thread:tool-progress",
413
+ payload: { threadId: thread.id, toolCallId, toolName, status },
377
414
  });
378
415
  }