@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,132 +1,56 @@
1
- import type {
2
- LanguageModelV3Prompt,
3
- LanguageModelV3Message,
4
- LanguageModelV3ToolResultPart,
5
- } from "@ai-sdk/provider";
6
- import { generateText } from "ai";
7
- import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
8
- import { estimateTokens } from "./token-estimator.ts";
9
- import { summarizeValue } from "./compressor.ts";
10
- import type { ContextConfig } from "./types.ts";
11
-
12
- type ToolRoleMessage = LanguageModelV3Message & { role: "tool" };
1
+ import { chat } from "@tanstack/ai";
2
+ import type { ModelMessage } from "@tanstack/ai";
3
+ import { createModel } from "@/ai/helpers";
4
+ import { estimateTokens } from "./token-estimator";
5
+ import { summarizeValue } from "./compressor";
6
+ import type { ContextConfig } from "./types";
13
7
 
14
8
  type SummarizationModelConfig = NonNullable<ContextConfig["summarizationModel"]>;
15
9
 
10
+ type ToolCallOrigin = { name: string; step: number };
11
+
16
12
  /**
17
- * Compresses tool results from older steps in the prompt.
18
- *
19
- * On step N, tool results from steps 1..N-2 are aggressively compressed
20
- * to short one-liners. Only the most recent tool result stays in full.
21
- * Does not mutate the input returns a new prompt array.
13
+ * A tool message names only the call it answers, so both the tool's own name and the
14
+ * step it belongs to have to be recovered from the assistant message that asked for
15
+ * it. One tool message carries one result, so a step that issued three calls in
16
+ * parallel arrives as three messages; regrouping them into their step is what stops
17
+ * a step's own results from ageing each other out.
22
18
  */
23
- export function compressIntraTurnToolResults(
24
- prompt: LanguageModelV3Prompt,
25
- toolResultMaxTokens: number,
26
- ) {
27
- // Collect indices of all tool-role messages
28
- const toolMessageIndices: number[] = [];
29
- for (let i = 0; i < prompt.length; i++) {
30
- if (prompt[i]!.role === "tool") {
31
- toolMessageIndices.push(i);
32
- }
33
- }
34
-
35
- // Need at least 3 tool messages to have "old" ones to compress
36
- // (keep last 1 fully intact, compress the rest)
37
- if (toolMessageIndices.length < 3) return prompt;
38
-
39
- // Clone the prompt so we don't mutate the original
40
- const result = prompt.map((msg) => ({ ...msg }));
19
+ function toolStepsOf(messages: ModelMessage[]) {
20
+ const calls = new Map<string, ToolCallOrigin>();
21
+ const steps: number[][] = [];
41
22
 
42
- // Compress all tool messages except the last one
43
- const indicesToCompress = toolMessageIndices.slice(0, -1);
44
-
45
- for (const idx of indicesToCompress) {
46
- const msg = result[idx] as ToolRoleMessage;
47
- const compressedContent = msg.content.map((part) => {
48
- if (part.type !== "tool-result") return part;
49
- return compressToolResultPart(part, toolResultMaxTokens);
50
- });
23
+ for (const [index, message] of messages.entries()) {
24
+ for (const call of message.toolCalls ?? []) {
25
+ calls.set(call.id, { name: call.function.name, step: steps.length });
26
+ }
27
+ if (message.role !== "tool") continue;
51
28
 
52
- result[idx] = { ...msg, content: compressedContent } as LanguageModelV3Message;
29
+ // A result whose call was never announced belongs to the step in progress;
30
+ // filing it anywhere else would invent a step that issued nothing.
31
+ const step = calls.get(message.toolCallId ?? "")?.step ?? steps.length;
32
+ (steps[step] ??= []).push(index);
53
33
  }
54
34
 
55
- return result;
56
- }
57
-
58
- function compressToolResultPart(
59
- part: LanguageModelV3ToolResultPart,
60
- maxTokens: number,
61
- ): LanguageModelV3ToolResultPart {
62
- const output = part.output;
63
-
64
- // Already small enough
65
- const outputStr = stringifyOutput(output);
66
- if (estimateTokens(outputStr) <= maxTokens) return part;
67
-
68
- const compressed = compressOutputToOneLiner(output, part.toolName);
69
-
70
- return {
71
- ...part,
72
- output: { type: "text", value: compressed },
73
- };
35
+ return { calls, steps };
74
36
  }
75
37
 
76
- function stringifyOutput(output: LanguageModelV3ToolResultPart["output"]): string {
77
- switch (output.type) {
78
- case "text":
79
- case "error-text":
80
- return output.value;
81
- case "json":
82
- case "error-json":
83
- return JSON.stringify(output.value);
84
- case "execution-denied":
85
- return output.reason ?? "denied";
86
- case "content":
87
- return JSON.stringify(output.value);
88
- default:
89
- return "";
90
- }
38
+ function toolNameOf(message: ModelMessage, calls: Map<string, ToolCallOrigin>) {
39
+ return calls.get(message.toolCallId ?? "")?.name ?? "tool";
91
40
  }
92
41
 
93
- function compressOutputToOneLiner(
94
- output: LanguageModelV3ToolResultPart["output"],
95
- toolName: string,
96
- ): string {
97
- const tag = `[COMPRESSED prior ${toolName} result]`;
42
+ /**
43
+ * A message's content is either a plain string or content parts, of which only the
44
+ * text ones can be digested — which is all a one-line stand-in was going to keep.
45
+ */
46
+ function contentText(content: ModelMessage["content"]) {
47
+ if (typeof content === "string") return content;
48
+ if (content === null) return "";
98
49
 
99
- switch (output.type) {
100
- case "text": {
101
- const preview = extractPreview(output.value);
102
- return `${tag} ${preview}`;
103
- }
104
- case "json":
105
- return `${tag} ${summarizeValue(output.value)}`;
106
- case "error-text": {
107
- const firstLine = output.value.split("\n")[0] ?? "error";
108
- return `${tag} ERROR - ${firstLine}`;
109
- }
110
- case "error-json": {
111
- const errStr = JSON.stringify(output.value).slice(0, 200);
112
- return `${tag} ERROR - ${errStr}`;
113
- }
114
- case "execution-denied":
115
- return `${tag} execution denied${output.reason ? ` - ${output.reason}` : ""}`;
116
- case "content": {
117
- const textParts = output.value
118
- .filter((p): p is Extract<typeof p, { type: "text" }> => p.type === "text")
119
- .map((p) => p.text);
120
- const joined = textParts.join(" ");
121
- const preview = extractPreview(joined);
122
- return `${tag} ${preview}`;
123
- }
124
- default:
125
- return `${tag} completed`;
126
- }
50
+ return content.flatMap((part) => (part.type === "text" ? [part.content] : [])).join(" ");
127
51
  }
128
52
 
129
- function extractPreview(text: string): string {
53
+ function extractPreview(text: string) {
130
54
  // Try to parse as JSON for structured summary
131
55
  try {
132
56
  const parsed: unknown = JSON.parse(text);
@@ -141,124 +65,117 @@ function extractPreview(text: string): string {
141
65
  return text.slice(0, 147) + "...";
142
66
  }
143
67
 
68
+ /**
69
+ * Compresses tool results from older steps in the prompt.
70
+ *
71
+ * On step N, tool results from steps 1..N-2 are aggressively compressed
72
+ * to short one-liners. Only the most recent step's results stay in full.
73
+ * Does not mutate the input — returns a new array.
74
+ */
75
+ export function compressIntraTurnToolResults(
76
+ messages: ModelMessage[],
77
+ toolResultMaxTokens: number,
78
+ ) {
79
+ const { calls, steps } = toolStepsOf(messages);
80
+
81
+ // Below three there is nothing genuinely "old" to compress once the most
82
+ // recent step is kept intact.
83
+ if (steps.length < 3) return messages;
84
+
85
+ const result = [...messages];
86
+
87
+ for (const index of steps.slice(0, -1).flat()) {
88
+ const message = messages[index]!;
89
+ const text = contentText(message.content);
90
+ if (estimateTokens(text) <= toolResultMaxTokens) continue;
91
+
92
+ const tag = `[COMPRESSED prior ${toolNameOf(message, calls)} result]`;
93
+ result[index] = { ...message, content: `${tag} ${extractPreview(text)}` };
94
+ }
95
+
96
+ return result;
97
+ }
98
+
144
99
  /**
145
100
  * Estimates the total token count of tool results in the prompt.
146
101
  */
147
- export function estimateToolResultTokens(prompt: LanguageModelV3Prompt): number {
102
+ export function estimateToolResultTokens(messages: ModelMessage[]) {
148
103
  let total = 0;
149
-
150
- for (const msg of prompt) {
151
- if (msg.role !== "tool") continue;
152
- for (const part of msg.content) {
153
- if (part.type === "tool-result") {
154
- total += estimateTokens(stringifyOutput(part.output));
155
- }
156
- }
104
+ for (const message of messages) {
105
+ if (message.role === "tool") total += estimateTokens(contentText(message.content));
157
106
  }
158
107
 
159
108
  return total;
160
109
  }
161
110
 
111
+ const SUMMARIZER_SYSTEM_PROMPT = `You are a tool result summarizer. Given previous tool call results from an AI agent's workflow, produce a concise summary preserving:
112
+ - Key data values, IDs, and counts
113
+ - Success/failure status of each operation
114
+ - Any error messages
115
+ - Data that subsequent tool calls may need
116
+
117
+ Maximum 300 tokens. Use bullet points. No preamble.`;
118
+
119
+ function requestSummary(resultTexts: string[], modelConfig: SummarizationModelConfig) {
120
+ return chat({
121
+ adapter: createModel(modelConfig, { name: "summarization" }),
122
+ systemPrompts: [SUMMARIZER_SYSTEM_PROMPT],
123
+ messages: [
124
+ {
125
+ role: "user",
126
+ content: `Summarize these tool results:\n\n${resultTexts.join("\n\n")}`,
127
+ },
128
+ ],
129
+ stream: false,
130
+ });
131
+ }
132
+
162
133
  /**
163
134
  * When accumulated tool result tokens exceed the threshold, summarizes
164
135
  * older step results using a lightweight LLM call.
165
136
  *
166
- * Replaces all tool results except the last two with a single synthetic
167
- * summary in the oldest tool message.
137
+ * Replaces every tool result before the last two steps with a single
138
+ * synthetic summary in the oldest tool message.
168
139
  */
169
140
  export async function summarizeOldStepResults(
170
- prompt: LanguageModelV3Prompt,
141
+ messages: ModelMessage[],
171
142
  thresholdTokens: number,
172
143
  modelConfig: SummarizationModelConfig,
173
144
  ) {
174
- const totalToolTokens = estimateToolResultTokens(prompt);
175
- if (totalToolTokens <= thresholdTokens) return prompt;
145
+ if (estimateToolResultTokens(messages) <= thresholdTokens) return messages;
176
146
 
177
- // Collect tool-role message indices
178
- const toolMessageIndices: number[] = [];
179
- for (let i = 0; i < prompt.length; i++) {
180
- if (prompt[i]!.role === "tool") {
181
- toolMessageIndices.push(i);
182
- }
183
- }
147
+ const { calls, steps } = toolStepsOf(messages);
184
148
 
185
- // Keep the last 2 tool messages intact, summarize the rest
186
- if (toolMessageIndices.length < 3) return prompt;
149
+ // Summarization keeps the last two steps intact one more than compression
150
+ // does, because a summary loses more detail than a truncated one-liner.
151
+ if (steps.length < 3) return messages;
187
152
 
188
- const indicesToSummarize = toolMessageIndices.slice(0, -2);
153
+ // An emptied tool message has nothing to contribute to the digest, and nothing
154
+ // to gain from being pointed at one.
155
+ const indices = steps
156
+ .slice(0, -2)
157
+ .flat()
158
+ .filter((index) => contentText(messages[index]!.content) !== "");
159
+ if (indices.length === 0) return messages;
189
160
 
190
- // Collect tool result text from older steps
191
- const resultTexts: string[] = [];
192
- for (const idx of indicesToSummarize) {
193
- const msg = prompt[idx] as ToolRoleMessage;
194
- for (const part of msg.content) {
195
- if (part.type === "tool-result") {
196
- resultTexts.push(`[${part.toolName}]: ${stringifyOutput(part.output)}`);
197
- }
198
- }
199
- }
200
-
201
- if (resultTexts.length === 0) return prompt;
202
-
203
- // Summarize with LLM
204
- const provider = createOpenAICompatible({
205
- name: modelConfig.providerName ?? "summarization-provider",
206
- baseURL: modelConfig.baseURL,
207
- apiKey: modelConfig.apiKey,
161
+ const resultTexts = indices.map((index) => {
162
+ const message = messages[index]!;
163
+ return `[${toolNameOf(message, calls)}]: ${contentText(message.content)}`;
208
164
  });
209
165
 
210
- const model = provider.chatModel(modelConfig.modelName);
166
+ const summary = await requestSummary(resultTexts, modelConfig);
211
167
 
212
- const { text: summary } = await generateText({
213
- model,
214
- system: `You are a tool result summarizer. Given previous tool call results from an AI agent's workflow, produce a concise summary preserving:
215
- - Key data values, IDs, and counts
216
- - Success/failure status of each operation
217
- - Any error messages
218
- - Data that subsequent tool calls may need
168
+ // The first old tool message carries the summary and the rest point at it, so
169
+ // the model reads the digest once rather than once per collapsed message.
170
+ const result = [...messages];
171
+ indices.forEach((index, position) => {
172
+ const content =
173
+ position === 0
174
+ ? `[Summary of ${indices.length} earlier tool results]:\n${summary}`
175
+ : "[see summary above]";
219
176
 
220
- Maximum 300 tokens. Use bullet points. No preamble.`,
221
- prompt: `Summarize these tool results:\n\n${resultTexts.join("\n\n")}`,
177
+ result[index] = { ...messages[index]!, content };
222
178
  });
223
179
 
224
- // Replace old tool messages with collapsed versions containing just the summary
225
- const result = [...prompt];
226
-
227
- for (let i = 0; i < indicesToSummarize.length; i++) {
228
- const idx = indicesToSummarize[i]!;
229
- const originalMsg = prompt[idx] as ToolRoleMessage;
230
-
231
- if (i === 0) {
232
- // First old tool message gets the summary
233
- const summarizedContent = originalMsg.content.map((part) => {
234
- if (part.type !== "tool-result") return part;
235
- return {
236
- ...part,
237
- output: {
238
- type: "text" as const,
239
- value: `[Summary of ${resultTexts.length} earlier tool results]:\n${summary}`,
240
- },
241
- };
242
- });
243
-
244
- // Keep only the first tool-result part with the summary
245
- const firstResultIdx = summarizedContent.findIndex((p) => p.type === "tool-result");
246
- const collapsedContent =
247
- firstResultIdx >= 0 ? [summarizedContent[firstResultIdx]!] : summarizedContent;
248
-
249
- result[idx] = { ...originalMsg, content: collapsedContent } as LanguageModelV3Message;
250
- } else {
251
- // Subsequent old tool messages get one-liners
252
- const collapsedContent = originalMsg.content.map((part) => {
253
- if (part.type !== "tool-result") return part;
254
- return {
255
- ...part,
256
- output: { type: "text" as const, value: `[see summary above]` },
257
- };
258
- });
259
- result[idx] = { ...originalMsg, content: collapsedContent } as LanguageModelV3Message;
260
- }
261
- }
262
-
263
180
  return result;
264
181
  }
@@ -0,0 +1,50 @@
1
+ import type { ResolvedCortexAgentConfig } from "@/config";
2
+ import type { ChatMessage, Thread } from "@/types";
3
+ import type { ThreadContextMeta } from "./types";
4
+ import { estimateMessagesTokens } from "./token-estimator";
5
+ import { summarizeMessages } from "./summarizer";
6
+
7
+ /**
8
+ * Post-response context optimization, called fire-and-forget from onFinish.
9
+ *
10
+ * Below the summarization threshold this only refreshes the token estimate —
11
+ * summarizing early would burn a model call to compress context that still fits.
12
+ */
13
+ export async function optimizeThreadContext(
14
+ thread: Thread,
15
+ messages: ChatMessage[],
16
+ config: ResolvedCortexAgentConfig,
17
+ ) {
18
+ const contextConfig = config.context;
19
+ const estimates = estimateMessagesTokens(messages);
20
+ const totalEstimatedTokens = estimates.reduce((sum, e) => sum + e.tokens, 0);
21
+ const threshold = contextConfig.maxContextTokens * contextConfig.summarizationThreshold;
22
+
23
+ if (totalEstimatedTokens <= threshold) {
24
+ await config.db.threads.updateContextMeta(thread.id, {
25
+ summary: thread.contextMeta?.summary ?? null,
26
+ summaryUpToMessageId: thread.contextMeta?.summaryUpToMessageId ?? null,
27
+ totalEstimatedTokens,
28
+ lastOptimizedAt: new Date().toISOString(),
29
+ } satisfies ThreadContextMeta);
30
+ return;
31
+ }
32
+
33
+ // The most recent messages stay unsummarized — they're the hot context.
34
+ const recentCount = Math.min(contextConfig.recentMessagesToKeep, messages.length);
35
+ const messagesToSummarize = messages.slice(0, messages.length - recentCount);
36
+ if (messagesToSummarize.length === 0) return;
37
+
38
+ const summary = await summarizeMessages(
39
+ messagesToSummarize,
40
+ thread.contextMeta?.summary ?? null,
41
+ contextConfig.summarizationModel ?? config.model,
42
+ );
43
+
44
+ await config.db.threads.updateContextMeta(thread.id, {
45
+ summary,
46
+ summaryUpToMessageId: messagesToSummarize.at(-1)?.id ?? null,
47
+ totalEstimatedTokens,
48
+ lastOptimizedAt: new Date().toISOString(),
49
+ } satisfies ThreadContextMeta);
50
+ }
@@ -1,29 +1,29 @@
1
- import { generateText } from "ai";
2
- import type { UIMessage } from "ai";
3
- import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
4
- import type { ContextConfig } from "./types.ts";
1
+ import { chat } from "@tanstack/ai";
2
+ import { createModel } from "@/ai/helpers";
3
+ import type { ChatMessage } from "@/types";
4
+ import type { ContextConfig } from "./types";
5
5
 
6
6
  type SummarizationModelConfig = NonNullable<ContextConfig["summarizationModel"]>;
7
7
 
8
- export async function summarizeMessages(
9
- messages: UIMessage[],
8
+ const SYSTEM_PROMPT = `You are a precise conversation summarizer. Produce a concise summary that preserves:
9
+ - Key decisions and conclusions
10
+ - Important entities (names, IDs, URLs, values)
11
+ - User intent and goals
12
+ - Any unresolved questions or next steps
13
+
14
+ Maximum 500 tokens. Use bullet points. Do not include preamble.`;
15
+
16
+ export function summarizeMessages(
17
+ messages: ChatMessage[],
10
18
  existingSummary: string | null,
11
19
  modelConfig: SummarizationModelConfig,
12
20
  ) {
13
- const provider = createOpenAICompatible({
14
- name: modelConfig.providerName ?? "summarization-provider",
15
- baseURL: modelConfig.baseURL,
16
- apiKey: modelConfig.apiKey,
17
- });
18
-
19
- const model = provider.chatModel(modelConfig.modelName);
20
-
21
21
  const conversationText = messages
22
22
  .map(function (msg) {
23
- const textParts = msg.parts
24
- .filter((p): p is Extract<typeof p, { type: "text" }> => p.type === "text")
25
- .map((p) => p.text);
26
- return `[${msg.role}]: ${textParts.join(" ")}`;
23
+ const text = msg.parts
24
+ .flatMap((part) => (part.type === "text" ? [part.content] : []))
25
+ .join(" ");
26
+ return `[${msg.role}]: ${text}`;
27
27
  })
28
28
  .join("\n");
29
29
 
@@ -31,20 +31,18 @@ export async function summarizeMessages(
31
31
  ? `\nPrior summary of earlier messages:\n${existingSummary}\n`
32
32
  : "";
33
33
 
34
- const { text } = await generateText({
35
- model,
36
- system: `You are a precise conversation summarizer. Produce a concise summary that preserves:
37
- - Key decisions and conclusions
38
- - Important entities (names, IDs, URLs, values)
39
- - User intent and goals
40
- - Any unresolved questions or next steps
41
-
42
- Maximum 500 tokens. Use bullet points. Do not include preamble.`,
43
- prompt: `${summaryContext}
34
+ return chat({
35
+ adapter: createModel(modelConfig, { name: "summarization" }),
36
+ systemPrompts: [SYSTEM_PROMPT],
37
+ messages: [
38
+ {
39
+ role: "user",
40
+ content: `${summaryContext}
44
41
  Summarize the following conversation:
45
42
 
46
43
  ${conversationText}`,
44
+ },
45
+ ],
46
+ stream: false,
47
47
  });
48
-
49
- return text;
50
48
  }
@@ -1,4 +1,5 @@
1
- import type { UIMessage } from "ai";
1
+ import type { MessagePart } from "@tanstack/ai";
2
+ import type { ChatMessage } from "@/types";
2
3
 
3
4
  /** Average characters per token for English text. Used by the heuristic estimator. */
4
5
  export const CHARS_PER_TOKEN = 4;
@@ -12,45 +13,49 @@ export function estimateTokens(text: string) {
12
13
  }
13
14
 
14
15
  /**
15
- * Estimates token count for a single UIMessage by walking its parts.
16
+ * Media and UI parts carry a source, not text. What the provider actually bills
17
+ * for them is set by its own tokenizer — tiles for an image, pages for a document —
18
+ * and the length of a base64 payload says nothing useful about it, so they are
19
+ * charged a flat rate instead.
16
20
  */
17
- export function estimateMessageTokens(message: UIMessage) {
18
- let tokens = 0;
19
-
20
- for (const part of message.parts) {
21
- if (part.type === "text" || part.type === "reasoning") {
22
- tokens += estimateTokens(part.text);
23
- } else if ("toolCallId" in part) {
24
- // Tool invocation parts (tool-${name})
25
- if ("input" in part && part.input != null) {
26
- tokens += estimateTokens(JSON.stringify(part.input));
27
- }
28
- if ("output" in part && part.output != null) {
29
- tokens += estimateTokens(JSON.stringify(part.output));
30
- }
31
- } else if (part.type === "source-url") {
32
- tokens += estimateTokens(part.url);
33
- } else if (part.type === "source-document") {
34
- tokens += estimateTokens(part.title);
35
- } else if (part.type === "file") {
36
- tokens += estimateTokens(part.url);
37
- } else {
38
- // step-start, data parts, etc.
39
- tokens += 5;
21
+ const OPAQUE_PART_TOKENS = 5;
22
+
23
+ function estimatePartTokens(part: MessagePart) {
24
+ switch (part.type) {
25
+ case "text":
26
+ case "thinking":
27
+ return estimateTokens(part.content);
28
+ case "tool-call": {
29
+ // `arguments` is already the JSON the model emitted. `output` is only
30
+ // set for client-executed tools and for calls resolved after approval.
31
+ const output: unknown = part.output;
32
+ const outputTokens = output === undefined ? 0 : estimateTokens(JSON.stringify(output));
33
+
34
+ return estimateTokens(part.arguments) + outputTokens;
40
35
  }
36
+ case "tool-result":
37
+ return estimateTokens(
38
+ typeof part.content === "string" ? part.content : JSON.stringify(part.content),
39
+ );
40
+ case "structured-output":
41
+ return estimateTokens(part.raw);
42
+ default:
43
+ return OPAQUE_PART_TOKENS;
41
44
  }
45
+ }
42
46
 
43
- // Per-message overhead (role, metadata framing)
44
- tokens += 4;
47
+ export function estimateMessageTokens(message: ChatMessage) {
48
+ const parts = message.parts.reduce((sum, part) => sum + estimatePartTokens(part), 0);
45
49
 
46
- return tokens;
50
+ // Per-message overhead (role, metadata framing)
51
+ return parts + 4;
47
52
  }
48
53
 
49
54
  /**
50
- * Estimates token counts for an array of UIMessages.
55
+ * Estimates token counts for an array of messages.
51
56
  * Returns per-message estimates in the same order.
52
57
  */
53
- export function estimateMessagesTokens(messages: UIMessage[]) {
58
+ export function estimateMessagesTokens(messages: ChatMessage[]) {
54
59
  return messages.map(function (message) {
55
60
  return {
56
61
  message,
@@ -1,4 +1,5 @@
1
- import type { ResolvedCortexAgentConfig } from "../config.ts";
1
+ import type { ResolvedCortexAgentConfig } from "@/config";
2
+ import { resolveBackendUrl } from "./backend-url";
2
3
 
3
4
  export async function fetchBackend(
4
5
  path: string,
@@ -14,18 +15,22 @@ export async function fetchBackend(
14
15
  finalOptions?.body &&
15
16
  typeof finalOptions.body === "string"
16
17
  ) {
18
+ let parsed: Record<string, unknown> | undefined;
17
19
  try {
18
- const parsed = JSON.parse(finalOptions.body) as Record<string, unknown>;
20
+ parsed = JSON.parse(finalOptions.body) as Record<string, unknown>;
21
+ } catch {
22
+ // Non-JSON bodies pass through unchanged.
23
+ }
24
+
25
+ if (parsed !== undefined) {
19
26
  const transformed = await backendFetch.transformRequestBody(parsed, {
20
27
  token,
21
28
  });
22
29
  finalOptions = { ...finalOptions, body: JSON.stringify(transformed) };
23
- } catch {
24
- // If body isn't valid JSON, pass through as-is
25
30
  }
26
31
  }
27
32
 
28
- return fetch(`${backendFetch.baseUrl}${path}`, {
33
+ return fetch(resolveBackendUrl(backendFetch.baseUrl, path), {
29
34
  ...finalOptions,
30
35
  headers: {
31
36
  "Content-Type": "application/json",