@goscribe/server 1.3.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/.env.example +12 -0
  2. package/.vscode/settings.json +3 -0
  3. package/REFACTOR_NOTES.md +60 -0
  4. package/TESTING_PROMPT.md +225 -0
  5. package/dist/controllers/admin.controller.d.ts +715 -0
  6. package/dist/controllers/admin.controller.js +9 -0
  7. package/dist/controllers/annotations.controller.d.ts +439 -0
  8. package/dist/controllers/annotations.controller.js +9 -0
  9. package/dist/controllers/app-router.controller.d.ts +3011 -0
  10. package/dist/controllers/app-router.controller.js +38 -0
  11. package/dist/controllers/app-router.controller.test.d.ts +1 -0
  12. package/dist/controllers/app-router.controller.test.js +36 -0
  13. package/dist/controllers/auth.controller.d.ts +323 -0
  14. package/dist/controllers/auth.controller.js +9 -0
  15. package/dist/controllers/base.controller.d.ts +4 -0
  16. package/dist/controllers/base.controller.js +5 -0
  17. package/dist/controllers/chat.controller.d.ts +341 -0
  18. package/dist/controllers/chat.controller.js +9 -0
  19. package/dist/controllers/copilot.controller.d.ts +397 -0
  20. package/dist/controllers/copilot.controller.js +9 -0
  21. package/dist/controllers/flashcards.controller.d.ts +651 -0
  22. package/dist/controllers/flashcards.controller.js +9 -0
  23. package/dist/controllers/members.controller.d.ts +339 -0
  24. package/dist/controllers/members.controller.js +9 -0
  25. package/dist/controllers/notifications.controller.d.ts +199 -0
  26. package/dist/controllers/notifications.controller.js +9 -0
  27. package/dist/controllers/payment.controller.d.ts +181 -0
  28. package/dist/controllers/payment.controller.js +9 -0
  29. package/dist/controllers/podcast.controller.d.ts +575 -0
  30. package/dist/controllers/podcast.controller.js +9 -0
  31. package/dist/controllers/router-module.controller.d.ts +5 -0
  32. package/dist/controllers/router-module.controller.js +6 -0
  33. package/dist/controllers/studyguide.controller.d.ts +73 -0
  34. package/dist/controllers/studyguide.controller.js +9 -0
  35. package/dist/controllers/worksheets.controller.d.ts +829 -0
  36. package/dist/controllers/worksheets.controller.js +9 -0
  37. package/dist/controllers/workspace.controller.d.ts +1207 -0
  38. package/dist/controllers/workspace.controller.js +9 -0
  39. package/dist/lib/activity_human_description.test.js +16 -15
  40. package/dist/lib/activity_log_service.test.js +28 -23
  41. package/dist/lib/ai/config.d.ts +20 -0
  42. package/dist/lib/ai/config.js +31 -0
  43. package/dist/lib/ai/embedding-client.d.ts +8 -0
  44. package/dist/lib/ai/embedding-client.js +30 -0
  45. package/dist/lib/ai/index.d.ts +47 -0
  46. package/dist/lib/ai/index.js +28 -0
  47. package/dist/lib/ai/inference-backend/client.d.ts +28 -0
  48. package/dist/lib/ai/inference-backend/client.js +301 -0
  49. package/dist/lib/ai/inference-backend/mocks.d.ts +12 -0
  50. package/dist/lib/ai/inference-backend/mocks.js +133 -0
  51. package/dist/lib/ai/inference-backend/types.d.ts +44 -0
  52. package/dist/lib/ai/inference-backend/types.js +1 -0
  53. package/dist/lib/ai/json-parse.d.ts +2 -0
  54. package/dist/lib/ai/json-parse.js +34 -0
  55. package/dist/lib/ai/llm-client.d.ts +6 -0
  56. package/dist/lib/ai/llm-client.js +19 -0
  57. package/dist/lib/ai/mock.d.ts +2 -0
  58. package/dist/lib/ai/mock.js +10 -0
  59. package/dist/lib/ai/types.d.ts +9 -0
  60. package/dist/lib/ai/types.js +1 -0
  61. package/dist/lib/chunking.d.ts +19 -0
  62. package/dist/lib/chunking.js +47 -0
  63. package/dist/lib/curated-kb-seed.d.ts +12 -0
  64. package/dist/lib/curated-kb-seed.js +155 -0
  65. package/dist/lib/email.js +67 -108
  66. package/dist/lib/embeddings.d.ts +2 -0
  67. package/dist/lib/embeddings.js +1 -0
  68. package/dist/lib/ensure-curated-kb-catalog.d.ts +6 -0
  69. package/dist/lib/ensure-curated-kb-catalog.js +53 -0
  70. package/dist/lib/env.d.ts +1 -5
  71. package/dist/lib/env.js +2 -7
  72. package/dist/lib/inference.d.ts +1 -8
  73. package/dist/lib/inference.js +1 -19
  74. package/dist/lib/kb-meta.d.ts +8 -0
  75. package/dist/lib/kb-meta.js +77 -0
  76. package/dist/lib/note-text.d.ts +1 -0
  77. package/dist/lib/note-text.js +47 -0
  78. package/dist/lib/notification-service.test.js +37 -36
  79. package/dist/lib/pdf.d.ts +11 -0
  80. package/dist/lib/pdf.js +11 -0
  81. package/dist/lib/usage_service.d.ts +2 -1
  82. package/dist/lib/usage_service.js +30 -12
  83. package/dist/lib/worksheet-generation.js +4 -4
  84. package/dist/lib/worksheet-generation.test.js +32 -17
  85. package/dist/lib/workspace-kb.d.ts +5 -0
  86. package/dist/lib/workspace-kb.js +7 -0
  87. package/dist/models/controller-context.model.d.ts +8 -0
  88. package/dist/models/controller-context.model.js +1 -0
  89. package/dist/repositories/artifact.repository.d.ts +60 -0
  90. package/dist/repositories/artifact.repository.js +40 -0
  91. package/dist/repositories/base.repository.d.ts +14 -0
  92. package/dist/repositories/base.repository.js +14 -0
  93. package/dist/repositories/invitation.repository.d.ts +94 -0
  94. package/dist/repositories/invitation.repository.js +44 -0
  95. package/dist/repositories/notification.repository.d.ts +72 -0
  96. package/dist/repositories/notification.repository.js +44 -0
  97. package/dist/repositories/router-module.repository.d.ts +10 -0
  98. package/dist/repositories/router-module.repository.js +14 -0
  99. package/dist/repositories/user.repository.d.ts +74 -0
  100. package/dist/repositories/user.repository.js +37 -0
  101. package/dist/repositories/workspace-member.repository.d.ts +31 -0
  102. package/dist/repositories/workspace-member.repository.js +31 -0
  103. package/dist/repositories/workspace.repository.d.ts +97 -0
  104. package/dist/repositories/workspace.repository.js +79 -0
  105. package/dist/routers/_app.d.ts +528 -33
  106. package/dist/routers/_app.js +4 -0
  107. package/dist/routers/admin.d.ts +0 -4
  108. package/dist/routers/admin.js +21 -549
  109. package/dist/routers/annotations.js +12 -170
  110. package/dist/routers/artifactVersions.d.ts +65 -0
  111. package/dist/routers/artifactVersions.js +14 -0
  112. package/dist/routers/auth.d.ts +0 -6
  113. package/dist/routers/auth.js +36 -421
  114. package/dist/routers/chat.js +15 -229
  115. package/dist/routers/copilot.d.ts +14 -13
  116. package/dist/routers/copilot.js +13 -532
  117. package/dist/routers/flashcards.d.ts +5 -5
  118. package/dist/routers/flashcards.js +23 -349
  119. package/dist/routers/knowledgeBase.d.ts +421 -0
  120. package/dist/routers/knowledgeBase.js +118 -0
  121. package/dist/routers/members.d.ts +0 -41
  122. package/dist/routers/members.js +22 -710
  123. package/dist/routers/notes.d.ts +94 -0
  124. package/dist/routers/notes.js +37 -0
  125. package/dist/routers/notifications.js +7 -109
  126. package/dist/routers/payment.d.ts +3 -2
  127. package/dist/routers/payment.js +11 -393
  128. package/dist/routers/podcast.d.ts +1 -1
  129. package/dist/routers/podcast.js +11 -784
  130. package/dist/routers/studyguide.js +3 -129
  131. package/dist/routers/worksheets.d.ts +29 -14
  132. package/dist/routers/worksheets.js +49 -628
  133. package/dist/routers/workspace.d.ts +0 -4
  134. package/dist/routers/workspace.js +28 -922
  135. package/dist/scripts/purge-deleted-users.js +2 -2
  136. package/dist/server.js +10 -3
  137. package/dist/services/activity/activity-human-description.service.d.ts +13 -0
  138. package/dist/services/activity/activity-human-description.service.js +221 -0
  139. package/dist/services/activity/activity-human-description.service.test.d.ts +1 -0
  140. package/dist/services/activity/activity-human-description.service.test.js +16 -0
  141. package/dist/services/activity/activity-log.service.d.ts +87 -0
  142. package/dist/services/activity/activity-log.service.js +276 -0
  143. package/dist/services/activity/activity-log.service.test.d.ts +1 -0
  144. package/dist/services/activity/activity-log.service.test.js +27 -0
  145. package/dist/services/activity-human-description.service.d.ts +13 -0
  146. package/dist/services/activity-human-description.service.js +221 -0
  147. package/dist/services/activity-human-description.service.test.d.ts +1 -0
  148. package/dist/services/activity-human-description.service.test.js +16 -0
  149. package/dist/services/activity-log.service.d.ts +87 -0
  150. package/dist/services/activity-log.service.js +276 -0
  151. package/dist/services/activity-log.service.test.d.ts +1 -0
  152. package/dist/services/activity-log.service.test.js +27 -0
  153. package/dist/services/admin/admin.service.d.ts +270 -0
  154. package/dist/services/admin/admin.service.js +476 -0
  155. package/dist/services/admin.service.d.ts +270 -0
  156. package/dist/services/admin.service.js +476 -0
  157. package/dist/services/ai/ai-session.service.d.ts +5 -0
  158. package/dist/services/ai/ai-session.service.js +4 -0
  159. package/dist/services/ai-session.service.d.ts +60 -0
  160. package/dist/services/ai-session.service.js +561 -0
  161. package/dist/services/annotation.service.d.ts +177 -0
  162. package/dist/services/annotation.service.js +154 -0
  163. package/dist/services/artifact-notification.service.d.ts +14 -0
  164. package/dist/services/artifact-notification.service.js +20 -0
  165. package/dist/services/artifact-version.service.d.ts +38 -0
  166. package/dist/services/artifact-version.service.js +129 -0
  167. package/dist/services/artifacts/annotation.service.d.ts +177 -0
  168. package/dist/services/artifacts/annotation.service.js +154 -0
  169. package/dist/services/artifacts/artifact-version.service.d.ts +38 -0
  170. package/dist/services/artifacts/artifact-version.service.js +129 -0
  171. package/dist/services/artifacts/chat.service.d.ts +127 -0
  172. package/dist/services/artifacts/chat.service.js +182 -0
  173. package/dist/services/artifacts/study-guide.service.d.ts +18 -0
  174. package/dist/services/artifacts/study-guide.service.js +65 -0
  175. package/dist/services/auth/auth.service.d.ts +94 -0
  176. package/dist/services/auth/auth.service.js +368 -0
  177. package/dist/services/auth.service.d.ts +94 -0
  178. package/dist/services/auth.service.js +368 -0
  179. package/dist/services/base.service.d.ts +14 -0
  180. package/dist/services/base.service.js +14 -0
  181. package/dist/services/billing/payment.service.d.ts +55 -0
  182. package/dist/services/billing/payment.service.js +368 -0
  183. package/dist/services/billing/subscription.service.d.ts +37 -0
  184. package/dist/services/billing/subscription.service.js +654 -0
  185. package/dist/services/billing/usage.service.d.ts +27 -0
  186. package/dist/services/billing/usage.service.js +77 -0
  187. package/dist/services/chat.service.d.ts +127 -0
  188. package/dist/services/chat.service.js +182 -0
  189. package/dist/services/content/copilot.service.d.ts +113 -0
  190. package/dist/services/content/copilot.service.js +453 -0
  191. package/dist/services/content/flashcard-progress.service.d.ts +159 -0
  192. package/dist/services/content/flashcard-progress.service.js +432 -0
  193. package/dist/services/content/flashcard.service.d.ts +140 -0
  194. package/dist/services/content/flashcard.service.js +326 -0
  195. package/dist/services/content/media-analysis.service.d.ts +23 -0
  196. package/dist/services/content/media-analysis.service.js +404 -0
  197. package/dist/services/content/podcast.service.d.ts +267 -0
  198. package/dist/services/content/podcast.service.js +653 -0
  199. package/dist/services/content/worksheet-content.service.d.ts +37 -0
  200. package/dist/services/content/worksheet-content.service.js +84 -0
  201. package/dist/services/content/worksheet-content.service.test.d.ts +1 -0
  202. package/dist/services/content/worksheet-content.service.test.js +69 -0
  203. package/dist/services/content/worksheet-generation.service.d.ts +91 -0
  204. package/dist/services/content/worksheet-generation.service.js +95 -0
  205. package/dist/services/content/worksheet-generation.service.test.d.ts +1 -0
  206. package/dist/services/content/worksheet-generation.service.test.js +20 -0
  207. package/dist/services/content/worksheet.service.d.ts +347 -0
  208. package/dist/services/content/worksheet.service.js +599 -0
  209. package/dist/services/copilot.service.d.ts +116 -0
  210. package/dist/services/copilot.service.js +447 -0
  211. package/dist/services/flashcard-progress.service.d.ts +2 -2
  212. package/dist/services/flashcard-progress.service.js +3 -2
  213. package/dist/services/flashcard.service.d.ts +140 -0
  214. package/dist/services/flashcard.service.js +325 -0
  215. package/dist/services/invitation.service.d.ts +66 -0
  216. package/dist/services/invitation.service.js +348 -0
  217. package/dist/services/knowledge/knowledge-base.service.d.ts +316 -0
  218. package/dist/services/knowledge/knowledge-base.service.js +544 -0
  219. package/dist/services/knowledge-base.service.d.ts +316 -0
  220. package/dist/services/knowledge-base.service.js +536 -0
  221. package/dist/services/media-analysis.service.d.ts +23 -0
  222. package/dist/services/media-analysis.service.js +384 -0
  223. package/dist/services/member.service.d.ts +36 -0
  224. package/dist/services/member.service.js +193 -0
  225. package/dist/services/members/invitation.service.d.ts +66 -0
  226. package/dist/services/members/invitation.service.js +348 -0
  227. package/dist/services/members/member.service.d.ts +36 -0
  228. package/dist/services/members/member.service.js +193 -0
  229. package/dist/services/note.service.d.ts +55 -0
  230. package/dist/services/note.service.js +111 -0
  231. package/dist/services/notification.service.d.ts +214 -0
  232. package/dist/services/notification.service.js +550 -0
  233. package/dist/services/notification.service.test.d.ts +1 -0
  234. package/dist/services/notification.service.test.js +87 -0
  235. package/dist/services/notifications/notification.service.d.ts +214 -0
  236. package/dist/services/notifications/notification.service.js +550 -0
  237. package/dist/services/notifications/notification.service.test.d.ts +1 -0
  238. package/dist/services/notifications/notification.service.test.js +87 -0
  239. package/dist/services/payment.service.d.ts +55 -0
  240. package/dist/services/payment.service.js +368 -0
  241. package/dist/services/podcast.service.d.ts +267 -0
  242. package/dist/services/podcast.service.js +654 -0
  243. package/dist/services/router-module.service.d.ts +7 -0
  244. package/dist/services/router-module.service.js +10 -0
  245. package/dist/services/study-guide.service.d.ts +18 -0
  246. package/dist/services/study-guide.service.js +65 -0
  247. package/dist/services/subscription.service.d.ts +37 -0
  248. package/dist/services/subscription.service.js +654 -0
  249. package/dist/services/usage-limit-policy.service.d.ts +12 -0
  250. package/dist/services/usage-limit-policy.service.js +22 -0
  251. package/dist/services/usage-limit-policy.service.test.d.ts +1 -0
  252. package/dist/services/usage-limit-policy.service.test.js +46 -0
  253. package/dist/services/usage.service.d.ts +27 -0
  254. package/dist/services/usage.service.js +77 -0
  255. package/dist/services/worksheet-content.service.d.ts +42 -0
  256. package/dist/services/worksheet-content.service.js +84 -0
  257. package/dist/services/worksheet-content.service.test.d.ts +1 -0
  258. package/dist/services/worksheet-content.service.test.js +69 -0
  259. package/dist/services/worksheet-generation.service.d.ts +91 -0
  260. package/dist/services/worksheet-generation.service.js +95 -0
  261. package/dist/services/worksheet-generation.service.test.d.ts +1 -0
  262. package/dist/services/worksheet-generation.service.test.js +20 -0
  263. package/dist/services/worksheet.service.d.ts +385 -0
  264. package/dist/services/worksheet.service.js +596 -0
  265. package/dist/services/workspace/workspace-analytics.service.d.ts +24 -0
  266. package/dist/services/workspace/workspace-analytics.service.js +95 -0
  267. package/dist/services/workspace/workspace-kb.service.d.ts +40 -0
  268. package/dist/services/workspace/workspace-kb.service.js +184 -0
  269. package/dist/services/workspace/workspace.service.d.ts +307 -0
  270. package/dist/services/workspace/workspace.service.js +394 -0
  271. package/dist/services/workspace-analytics.service.d.ts +24 -0
  272. package/dist/services/workspace-analytics.service.js +95 -0
  273. package/dist/services/workspace-kb.service.d.ts +40 -0
  274. package/dist/services/workspace-kb.service.js +184 -0
  275. package/dist/services/workspace-progress.service.d.ts +27 -0
  276. package/dist/services/workspace-progress.service.js +56 -0
  277. package/dist/services/workspace-progress.service.test.d.ts +1 -0
  278. package/dist/services/workspace-progress.service.test.js +49 -0
  279. package/dist/services/workspace.service.d.ts +307 -0
  280. package/dist/services/workspace.service.js +390 -0
  281. package/dist/trpc.js +2 -2
  282. package/package.json +5 -6
  283. package/prisma/migrations/20260509000001_add_knowledge_base/migration.sql +99 -0
  284. package/prisma/migrations/20260509000002_curate_knowledge_base/migration.sql +52 -0
  285. package/prisma/migrations/20260522000000_add_notes/migration.sql +27 -0
  286. package/prisma/migrations/20260524000000_remove_notes/migration.sql +3 -0
  287. package/prisma/schema.prisma +150 -48
  288. package/prisma/seed.mjs +67 -0
  289. package/scripts/debug/README.md +4 -0
  290. package/src/README.md +63 -0
  291. package/src/lib/ai/config.ts +34 -0
  292. package/src/lib/ai/embedding-client.ts +47 -0
  293. package/src/lib/ai/index.ts +62 -0
  294. package/src/lib/ai/inference-backend/client.ts +479 -0
  295. package/src/lib/ai/inference-backend/mocks.ts +171 -0
  296. package/src/lib/ai/inference-backend/types.ts +50 -0
  297. package/src/lib/ai/json-parse.ts +35 -0
  298. package/src/lib/ai/llm-client.ts +31 -0
  299. package/src/lib/ai/mock.ts +12 -0
  300. package/src/lib/ai/types.ts +11 -0
  301. package/src/lib/chunking.ts +81 -0
  302. package/src/lib/curated-kb-seed.ts +164 -0
  303. package/src/lib/email.ts +77 -115
  304. package/src/lib/embeddings.ts +9 -0
  305. package/src/lib/ensure-curated-kb-catalog.ts +60 -0
  306. package/src/lib/env.ts +2 -7
  307. package/src/lib/inference.ts +1 -21
  308. package/src/lib/kb-meta.ts +81 -0
  309. package/src/lib/pdf.ts +23 -0
  310. package/src/lib/workspace-kb.ts +7 -0
  311. package/src/repositories/artifact.repository.ts +55 -0
  312. package/src/repositories/base.repository.ts +19 -0
  313. package/src/repositories/invitation.repository.ts +53 -0
  314. package/src/repositories/notification.repository.ts +53 -0
  315. package/src/repositories/user.repository.ts +44 -0
  316. package/src/repositories/workspace-member.repository.ts +38 -0
  317. package/src/repositories/workspace.repository.ts +89 -0
  318. package/src/routers/_app.ts +4 -0
  319. package/src/routers/admin.ts +124 -692
  320. package/src/routers/annotations.ts +25 -203
  321. package/src/routers/artifactVersions.ts +32 -0
  322. package/src/routers/auth.ts +81 -519
  323. package/src/routers/chat.ts +42 -245
  324. package/src/routers/copilot.ts +41 -666
  325. package/src/routers/flashcards.ts +108 -404
  326. package/src/routers/knowledgeBase.ts +216 -0
  327. package/src/routers/members.ts +60 -782
  328. package/src/routers/notifications.ts +15 -117
  329. package/src/routers/payment.ts +37 -446
  330. package/src/routers/podcast.ts +36 -898
  331. package/src/routers/studyguide.ts +5 -144
  332. package/src/routers/worksheets.ts +171 -735
  333. package/src/routers/workspace.ts +138 -1109
  334. package/src/scripts/purge-deleted-users.ts +2 -2
  335. package/src/server.ts +10 -3
  336. package/src/{lib/activity_human_description.test.ts → services/activity/activity-human-description.service.test.ts} +1 -1
  337. package/src/{lib/activity_log_service.test.ts → services/activity/activity-log.service.test.ts} +1 -1
  338. package/src/{lib/activity_log_service.ts → services/activity/activity-log.service.ts} +2 -2
  339. package/src/services/admin/admin.service.ts +612 -0
  340. package/src/services/ai/ai-session.service.ts +5 -0
  341. package/src/services/artifacts/annotation.service.ts +189 -0
  342. package/src/services/artifacts/artifact-version.service.ts +151 -0
  343. package/src/services/artifacts/chat.service.ts +197 -0
  344. package/src/services/artifacts/study-guide.service.ts +72 -0
  345. package/src/services/auth/auth.service.ts +473 -0
  346. package/src/services/base.service.ts +19 -0
  347. package/src/services/billing/payment.service.ts +436 -0
  348. package/src/{lib/subscription_service.ts → services/billing/subscription.service.ts} +5 -5
  349. package/src/{lib/usage_service.ts → services/billing/usage.service.ts} +32 -12
  350. package/src/services/content/copilot.service.ts +596 -0
  351. package/src/services/{flashcard-progress.service.ts → content/flashcard-progress.service.ts} +6 -3
  352. package/src/services/content/flashcard.service.ts +394 -0
  353. package/src/services/content/media-analysis.service.ts +556 -0
  354. package/src/services/content/podcast.service.ts +777 -0
  355. package/src/services/content/worksheet-content.service.test.ts +83 -0
  356. package/src/services/content/worksheet-content.service.ts +117 -0
  357. package/src/{lib/worksheet-generation.test.ts → services/content/worksheet-generation.service.test.ts} +1 -1
  358. package/src/services/content/worksheet.service.ts +751 -0
  359. package/src/services/knowledge/knowledge-base.service.ts +705 -0
  360. package/src/services/members/invitation.service.ts +427 -0
  361. package/src/services/members/member.service.ts +241 -0
  362. package/src/{lib/notification-service.test.ts → services/notifications/notification.service.test.ts} +2 -2
  363. package/src/{lib/notification-service.ts → services/notifications/notification.service.ts} +102 -1
  364. package/src/services/workspace/workspace-analytics.service.ts +107 -0
  365. package/src/services/workspace/workspace-kb.service.ts +273 -0
  366. package/src/services/workspace/workspace.service.ts +481 -0
  367. package/src/trpc.ts +2 -2
  368. package/src/lib/ai-session.ts +0 -704
  369. package/src/lib/workspace-access.ts +0 -13
  370. /package/{check-difficulty.cjs → scripts/debug/check-difficulty.cjs} +0 -0
  371. /package/{check-questions.cjs → scripts/debug/check-questions.cjs} +0 -0
  372. /package/{db-summary.cjs → scripts/debug/db-summary.cjs} +0 -0
  373. /package/{mcq-test.cjs → scripts/debug/mcq-test.cjs} +0 -0
  374. /package/{test-generate.js → scripts/debug/test-generate.js} +0 -0
  375. /package/{test-ratio.cjs → scripts/debug/test-ratio.cjs} +0 -0
  376. /package/{zod-test.cjs → scripts/debug/zod-test.cjs} +0 -0
  377. /package/src/{lib/activity_human_description.ts → services/activity/activity-human-description.service.ts} +0 -0
  378. /package/src/{lib/worksheet-generation.ts → services/content/worksheet-generation.service.ts} +0 -0
@@ -0,0 +1,116 @@
1
+ import type { PrismaClient } from '@prisma/client';
2
+ import { z } from 'zod';
3
+ import { BaseService } from './base.service.js';
4
+ export declare const copilotArtifactType: z.ZodEnum<{
5
+ flashcards: "flashcards";
6
+ worksheet: "worksheet";
7
+ "study-guide": "study-guide";
8
+ notes: "notes";
9
+ }>;
10
+ export declare const copilotContextSchema: z.ZodObject<{
11
+ workspaceId: z.ZodString;
12
+ artifactId: z.ZodString;
13
+ artifactType: z.ZodEnum<{
14
+ flashcards: "flashcards";
15
+ worksheet: "worksheet";
16
+ "study-guide": "study-guide";
17
+ notes: "notes";
18
+ }>;
19
+ documentContent: z.ZodString;
20
+ selectedText: z.ZodOptional<z.ZodString>;
21
+ viewportText: z.ZodOptional<z.ZodString>;
22
+ cursorPosition: z.ZodOptional<z.ZodObject<{
23
+ start: z.ZodNumber;
24
+ end: z.ZodNumber;
25
+ }, z.core.$strip>>;
26
+ metadata: z.ZodOptional<z.ZodObject<{
27
+ flashcardId: z.ZodOptional<z.ZodString>;
28
+ questionId: z.ZodOptional<z.ZodString>;
29
+ documentId: z.ZodOptional<z.ZodString>;
30
+ }, z.core.$strip>>;
31
+ }, z.core.$strip>;
32
+ export type CopilotContextInput = z.infer<typeof copilotContextSchema>;
33
+ export declare class CopilotService extends BaseService {
34
+ constructor(db: PrismaClient);
35
+ private assertWorkspaceAccess;
36
+ private assertConversationAccess;
37
+ private loadHistory;
38
+ private persistConversationExchange;
39
+ listConversations(userId: string, workspaceId: string): Promise<{
40
+ id: string;
41
+ title: string;
42
+ updatedAt: Date;
43
+ preview: string;
44
+ }[]>;
45
+ getConversation(userId: string, workspaceId: string, conversationId: string): Promise<{
46
+ id: string;
47
+ title: string;
48
+ messages: {
49
+ id: string;
50
+ role: string;
51
+ content: string;
52
+ createdAt: Date;
53
+ }[];
54
+ }>;
55
+ createConversation(userId: string, input: {
56
+ workspaceId: string;
57
+ title?: string;
58
+ }): Promise<{
59
+ id: string;
60
+ title: string;
61
+ updatedAt: Date;
62
+ }>;
63
+ deleteConversation(userId: string, workspaceId: string, conversationId: string): Promise<{
64
+ success: boolean;
65
+ }>;
66
+ ask(userId: string, input: {
67
+ context: CopilotContextInput;
68
+ message: string;
69
+ conversationId?: string;
70
+ }): Promise<{
71
+ answer: string;
72
+ highlights: {
73
+ start: number;
74
+ end: number;
75
+ label?: string | undefined;
76
+ }[];
77
+ }>;
78
+ explainSelection(userId: string, input: {
79
+ context: CopilotContextInput;
80
+ message?: string;
81
+ conversationId?: string;
82
+ }): Promise<{
83
+ answer: string;
84
+ }>;
85
+ suggestHighlights(userId: string, input: {
86
+ context: CopilotContextInput;
87
+ message?: string;
88
+ conversationId?: string;
89
+ }): Promise<{
90
+ answer: string;
91
+ highlights: {
92
+ start: number;
93
+ end: number;
94
+ label?: string | undefined;
95
+ }[];
96
+ }>;
97
+ generateFlashcards(userId: string, input: {
98
+ context: CopilotContextInput;
99
+ message?: string;
100
+ numCards: number;
101
+ conversationId?: string;
102
+ }): Promise<{
103
+ answer: string;
104
+ artifactId: string;
105
+ flashcards: {
106
+ id: string;
107
+ createdAt: Date;
108
+ artifactId: string;
109
+ order: number;
110
+ front: string;
111
+ back: string;
112
+ tags: string[];
113
+ acceptedAnswers: string[];
114
+ }[];
115
+ }>;
116
+ }
@@ -0,0 +1,447 @@
1
+ import { TRPCError } from '@trpc/server';
2
+ import { z } from 'zod';
3
+ import { BaseService } from './base.service.js';
4
+ import inference from '../lib/inference.js';
5
+ import { sanitizeString } from '../lib/validation.js';
6
+ import { workspaceAccessWhere } from '../repositories/workspace.repository.js';
7
+ import PusherService from '../lib/pusher.js';
8
+ import { ArtifactType } from '../lib/constants.js';
9
+ export const copilotArtifactType = z.enum([
10
+ 'study-guide',
11
+ 'worksheet',
12
+ 'flashcards',
13
+ 'notes',
14
+ ]);
15
+ export const copilotContextSchema = z.object({
16
+ workspaceId: z.string().min(1),
17
+ artifactId: z.string().min(1),
18
+ artifactType: copilotArtifactType,
19
+ documentContent: z.string().min(1),
20
+ selectedText: z.string().optional(),
21
+ viewportText: z.string().optional(),
22
+ cursorPosition: z
23
+ .object({
24
+ start: z.number().int().min(0),
25
+ end: z.number().int().min(0),
26
+ })
27
+ .optional(),
28
+ metadata: z
29
+ .object({
30
+ flashcardId: z.string().optional(),
31
+ questionId: z.string().optional(),
32
+ documentId: z.string().optional(),
33
+ })
34
+ .optional(),
35
+ });
36
+ const highlightInstructionSchema = z.object({
37
+ start: z.number().int().min(0),
38
+ end: z.number().int().min(0),
39
+ label: z.string().optional(),
40
+ });
41
+ const flashcardSchema = z.object({
42
+ front: z.string().min(1),
43
+ back: z.string().min(1),
44
+ });
45
+ const RATE_LIMIT_WINDOW_MS = 60000;
46
+ const RATE_LIMIT_MAX_REQUESTS = 20;
47
+ const requestBuckets = new Map();
48
+ function enforceRateLimit(userId, workspaceId) {
49
+ const now = Date.now();
50
+ const key = `${userId}:${workspaceId}`;
51
+ const existing = requestBuckets.get(key) ?? [];
52
+ const recent = existing.filter((timestamp) => now - timestamp < RATE_LIMIT_WINDOW_MS);
53
+ if (recent.length >= RATE_LIMIT_MAX_REQUESTS) {
54
+ throw new TRPCError({
55
+ code: 'TOO_MANY_REQUESTS',
56
+ message: 'Too many copilot requests. Please wait a moment.',
57
+ });
58
+ }
59
+ recent.push(now);
60
+ requestBuckets.set(key, recent);
61
+ }
62
+ function contextWindow(context) {
63
+ return sanitizeString(context.documentContent, 5000000);
64
+ }
65
+ function extractJson(content) {
66
+ const fencedMatch = content.match(/```json\s*([\s\S]*?)```/i);
67
+ if (fencedMatch?.[1]) {
68
+ try {
69
+ return JSON.parse(fencedMatch[1]);
70
+ }
71
+ catch {
72
+ // fall through
73
+ }
74
+ }
75
+ const objectMatch = content.match(/\{[\s\S]*\}/);
76
+ if (objectMatch?.[0]) {
77
+ try {
78
+ return JSON.parse(objectMatch[0]);
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+ function getAnswerFromParsedJSON(parsed) {
87
+ if (!parsed)
88
+ return null;
89
+ const potentialKeys = [
90
+ 'answer',
91
+ 'summary',
92
+ 'explanation',
93
+ 'content',
94
+ 'explanationText',
95
+ 'response',
96
+ 'result',
97
+ ];
98
+ for (const key of potentialKeys) {
99
+ if (typeof parsed[key] === 'string' && parsed[key].length > 0) {
100
+ return parsed[key];
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+ async function callCopilotModel(params) {
106
+ const documentSnippet = contextWindow(params.context);
107
+ const selectedText = params.context.selectedText
108
+ ? sanitizeString(params.context.selectedText, 2000)
109
+ : '';
110
+ const viewportText = params.context.viewportText
111
+ ? sanitizeString(params.context.viewportText, 4000)
112
+ : '';
113
+ const message = sanitizeString(params.message, 4000);
114
+ const systemPrompt = [
115
+ 'You are an AI study assistant for the Scribe learning platform.',
116
+ 'Be concise, correct, and safe. Do not claim edits were applied unless asked and confirmed by the user.',
117
+ 'Return valid JSON only.',
118
+ '',
119
+ 'OUTPUT FORMAT:',
120
+ '{',
121
+ ' "answer": "markdown response for the user",',
122
+ ' "highlights": [{ "start": 0, "end": 10, "label": "optional" }],',
123
+ ' "flashcards": [{ "front": "question", "back": "answer" }]',
124
+ '}',
125
+ 'Include only relevant keys for the task.',
126
+ '',
127
+ `MODE: ${params.mode}`,
128
+ `ARTIFACT_TYPE: ${params.context.artifactType}`,
129
+ ].join('\n');
130
+ const contextPrompt = [
131
+ 'DOCUMENT:',
132
+ documentSnippet,
133
+ '',
134
+ 'SELECTION:',
135
+ selectedText || 'None',
136
+ '',
137
+ 'VIEWPORT_TEXT:',
138
+ viewportText || 'None',
139
+ ].join('\n');
140
+ const messages = [
141
+ { role: 'system', content: systemPrompt },
142
+ { role: 'user', content: contextPrompt },
143
+ ];
144
+ if (params.history && params.history.length > 0) {
145
+ params.history.forEach((msg) => {
146
+ messages.push({
147
+ role: msg.role === 'user' ? 'user' : 'assistant',
148
+ content: msg.content,
149
+ });
150
+ });
151
+ }
152
+ messages.push({ role: 'user', content: message });
153
+ const response = await inference(messages);
154
+ const content = response.choices?.[0]?.message?.content ?? '';
155
+ const parsed = extractJson(content);
156
+ return { rawContent: content, parsed };
157
+ }
158
+ export class CopilotService extends BaseService {
159
+ constructor(db) {
160
+ super(db);
161
+ }
162
+ async assertWorkspaceAccess(userId, workspaceId) {
163
+ const workspace = await this.db.workspace.findFirst({
164
+ where: { id: workspaceId, ...workspaceAccessWhere(userId) },
165
+ select: { id: true },
166
+ });
167
+ if (!workspace) {
168
+ throw new TRPCError({
169
+ code: 'FORBIDDEN',
170
+ message: 'You do not have access to this workspace',
171
+ });
172
+ }
173
+ }
174
+ async assertConversationAccess(userId, workspaceId, conversationId) {
175
+ const conversation = await this.db.copilotConversation.findFirst({
176
+ where: { id: conversationId, workspaceId, userId },
177
+ select: { id: true },
178
+ });
179
+ if (!conversation) {
180
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });
181
+ }
182
+ }
183
+ async loadHistory(conversationId) {
184
+ if (!conversationId)
185
+ return [];
186
+ const messages = await this.db.copilotMessage.findMany({
187
+ where: { conversationId },
188
+ orderBy: { createdAt: 'asc' },
189
+ take: 50,
190
+ });
191
+ return messages.map((m) => ({
192
+ role: m.role.toLowerCase(),
193
+ content: m.content,
194
+ }));
195
+ }
196
+ async persistConversationExchange(params) {
197
+ if (!params.conversationId)
198
+ return;
199
+ await this.assertConversationAccess(params.userId, params.workspaceId, params.conversationId);
200
+ const generatedTitle = sanitizeString(params.userMessage.replace(/\s+/g, ' ').trim(), 80);
201
+ const conversation = await this.db.copilotConversation.findUnique({
202
+ where: { id: params.conversationId },
203
+ select: { title: true },
204
+ });
205
+ const shouldUpdateTitle = !!conversation &&
206
+ conversation.title.trim().toLowerCase() === 'new chat' &&
207
+ generatedTitle.length > 0;
208
+ await this.db.copilotMessage.createMany({
209
+ data: [
210
+ {
211
+ conversationId: params.conversationId,
212
+ role: 'USER',
213
+ content: sanitizeString(params.userMessage, 8000),
214
+ },
215
+ {
216
+ conversationId: params.conversationId,
217
+ role: 'ASSISTANT',
218
+ content: sanitizeString(params.assistantMessage, 20000),
219
+ },
220
+ ],
221
+ });
222
+ await this.db.copilotConversation.update({
223
+ where: { id: params.conversationId },
224
+ data: {
225
+ updatedAt: new Date(),
226
+ ...(shouldUpdateTitle ? { title: generatedTitle } : {}),
227
+ },
228
+ });
229
+ }
230
+ async listConversations(userId, workspaceId) {
231
+ await this.assertWorkspaceAccess(userId, workspaceId);
232
+ const conversations = await this.db.copilotConversation.findMany({
233
+ where: { workspaceId, userId },
234
+ include: {
235
+ messages: { orderBy: { createdAt: 'desc' }, take: 1 },
236
+ },
237
+ orderBy: { updatedAt: 'desc' },
238
+ });
239
+ return conversations.map((conversation) => ({
240
+ id: conversation.id,
241
+ title: conversation.title,
242
+ updatedAt: conversation.updatedAt,
243
+ preview: conversation.messages[0]?.content ?? '',
244
+ }));
245
+ }
246
+ async getConversation(userId, workspaceId, conversationId) {
247
+ await this.assertWorkspaceAccess(userId, workspaceId);
248
+ await this.assertConversationAccess(userId, workspaceId, conversationId);
249
+ const conversation = await this.db.copilotConversation.findUnique({
250
+ where: { id: conversationId },
251
+ include: { messages: { orderBy: { createdAt: 'asc' } } },
252
+ });
253
+ if (!conversation) {
254
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });
255
+ }
256
+ return {
257
+ id: conversation.id,
258
+ title: conversation.title,
259
+ messages: conversation.messages.map((message) => ({
260
+ id: message.id,
261
+ role: message.role === 'USER' ? 'user' : 'assistant',
262
+ content: message.content,
263
+ createdAt: message.createdAt,
264
+ })),
265
+ };
266
+ }
267
+ async createConversation(userId, input) {
268
+ await this.assertWorkspaceAccess(userId, input.workspaceId);
269
+ const conversation = await this.db.copilotConversation.create({
270
+ data: {
271
+ workspaceId: input.workspaceId,
272
+ userId,
273
+ title: sanitizeString(input.title || 'New Chat', 120),
274
+ },
275
+ });
276
+ return {
277
+ id: conversation.id,
278
+ title: conversation.title,
279
+ updatedAt: conversation.updatedAt,
280
+ };
281
+ }
282
+ async deleteConversation(userId, workspaceId, conversationId) {
283
+ await this.assertWorkspaceAccess(userId, workspaceId);
284
+ await this.assertConversationAccess(userId, workspaceId, conversationId);
285
+ await this.db.copilotConversation.delete({ where: { id: conversationId } });
286
+ return { success: true };
287
+ }
288
+ async ask(userId, input) {
289
+ enforceRateLimit(userId, input.context.workspaceId);
290
+ await this.assertWorkspaceAccess(userId, input.context.workspaceId);
291
+ await PusherService.emitTaskComplete(input.context.workspaceId, 'copilot:thinking', {
292
+ status: 'started',
293
+ });
294
+ const history = await this.loadHistory(input.conversationId);
295
+ const model = await callCopilotModel({
296
+ mode: 'ask',
297
+ context: input.context,
298
+ message: input.message,
299
+ history,
300
+ });
301
+ const answer = getAnswerFromParsedJSON(model.parsed) ||
302
+ model.rawContent ||
303
+ 'I could not generate a response.';
304
+ const highlights = z.array(highlightInstructionSchema).safeParse(model.parsed?.highlights);
305
+ const safeHighlights = highlights.success
306
+ ? highlights.data.filter((item) => item.start < item.end &&
307
+ item.end <= Math.max(input.context.documentContent.length, 0))
308
+ : [];
309
+ await PusherService.emitTaskComplete(input.context.workspaceId, 'copilot:response', {
310
+ status: 'completed',
311
+ });
312
+ await this.persistConversationExchange({
313
+ userId,
314
+ workspaceId: input.context.workspaceId,
315
+ conversationId: input.conversationId,
316
+ userMessage: input.message,
317
+ assistantMessage: answer,
318
+ });
319
+ return { answer, highlights: safeHighlights };
320
+ }
321
+ async explainSelection(userId, input) {
322
+ enforceRateLimit(userId, input.context.workspaceId);
323
+ await this.assertWorkspaceAccess(userId, input.context.workspaceId);
324
+ await PusherService.emitTaskComplete(input.context.workspaceId, 'copilot:thinking', {
325
+ status: 'started',
326
+ });
327
+ const question = input.message && input.message.trim().length > 0
328
+ ? input.message
329
+ : 'Explain the selected text in simple study-friendly terms and include one practical example.';
330
+ const history = await this.loadHistory(input.conversationId);
331
+ const model = await callCopilotModel({
332
+ mode: 'explainSelection',
333
+ context: input.context,
334
+ message: question,
335
+ history,
336
+ });
337
+ const answer = getAnswerFromParsedJSON(model.parsed) ||
338
+ model.rawContent ||
339
+ 'I could not explain this selection.';
340
+ await PusherService.emitTaskComplete(input.context.workspaceId, 'copilot:response', {
341
+ status: 'completed',
342
+ });
343
+ await this.persistConversationExchange({
344
+ userId,
345
+ workspaceId: input.context.workspaceId,
346
+ conversationId: input.conversationId,
347
+ userMessage: question,
348
+ assistantMessage: answer,
349
+ });
350
+ return { answer };
351
+ }
352
+ async suggestHighlights(userId, input) {
353
+ enforceRateLimit(userId, input.context.workspaceId);
354
+ await this.assertWorkspaceAccess(userId, input.context.workspaceId);
355
+ await PusherService.emitTaskComplete(input.context.workspaceId, 'copilot:thinking', {
356
+ status: 'started',
357
+ });
358
+ const instruction = input.message && input.message.trim().length > 0
359
+ ? input.message
360
+ : 'Suggest key highlights for the provided section.';
361
+ const history = await this.loadHistory(input.conversationId);
362
+ const model = await callCopilotModel({
363
+ mode: 'suggestHighlights',
364
+ context: input.context,
365
+ message: instruction,
366
+ history,
367
+ });
368
+ const parsedHighlights = z
369
+ .array(highlightInstructionSchema)
370
+ .safeParse(model.parsed?.highlights);
371
+ const safeHighlights = parsedHighlights.success
372
+ ? parsedHighlights.data.filter((item) => item.start < item.end &&
373
+ item.end <= Math.max(input.context.documentContent.length, 0))
374
+ : [];
375
+ await PusherService.emitTaskComplete(input.context.workspaceId, 'copilot:highlight_suggestions', { highlights: safeHighlights });
376
+ const answer = getAnswerFromParsedJSON(model.parsed) || 'Here are suggested highlights.';
377
+ await this.persistConversationExchange({
378
+ userId,
379
+ workspaceId: input.context.workspaceId,
380
+ conversationId: input.conversationId,
381
+ userMessage: instruction,
382
+ assistantMessage: answer,
383
+ });
384
+ return { answer, highlights: safeHighlights };
385
+ }
386
+ async generateFlashcards(userId, input) {
387
+ enforceRateLimit(userId, input.context.workspaceId);
388
+ await this.assertWorkspaceAccess(userId, input.context.workspaceId);
389
+ await PusherService.emitTaskComplete(input.context.workspaceId, 'copilot:thinking', {
390
+ status: 'started',
391
+ });
392
+ const instruction = input.message && input.message.trim().length > 0
393
+ ? input.message
394
+ : `Generate ${input.numCards} concise study flashcards from this context.`;
395
+ const history = await this.loadHistory(input.conversationId);
396
+ const model = await callCopilotModel({
397
+ mode: 'generateFlashcards',
398
+ context: input.context,
399
+ message: instruction,
400
+ history,
401
+ });
402
+ const parsedFlashcards = z.array(flashcardSchema).safeParse(model.parsed?.flashcards);
403
+ const cards = (parsedFlashcards.success ? parsedFlashcards.data : []).slice(0, input.numCards);
404
+ if (cards.length === 0) {
405
+ throw new TRPCError({
406
+ code: 'BAD_REQUEST',
407
+ message: 'Copilot did not return valid flashcards.',
408
+ });
409
+ }
410
+ const artifact = await this.db.artifact.create({
411
+ data: {
412
+ workspaceId: input.context.workspaceId,
413
+ type: ArtifactType.FLASHCARD_SET,
414
+ title: `Copilot Flashcards - ${new Date().toLocaleString()}`,
415
+ createdById: userId,
416
+ generating: false,
417
+ flashcards: {
418
+ create: cards.map((card, index) => ({
419
+ front: sanitizeString(card.front, 200),
420
+ back: sanitizeString(card.back, 500),
421
+ order: index,
422
+ tags: ['copilot-generated'],
423
+ })),
424
+ },
425
+ },
426
+ include: { flashcards: true },
427
+ });
428
+ await PusherService.emitTaskComplete(input.context.workspaceId, 'copilot:response', {
429
+ status: 'completed',
430
+ flashcardSetId: artifact.id,
431
+ });
432
+ const answer = getAnswerFromParsedJSON(model.parsed) ||
433
+ `Generated ${artifact.flashcards.length} flashcards.`;
434
+ await this.persistConversationExchange({
435
+ userId,
436
+ workspaceId: input.context.workspaceId,
437
+ conversationId: input.conversationId,
438
+ userMessage: instruction,
439
+ assistantMessage: answer,
440
+ });
441
+ return {
442
+ answer,
443
+ artifactId: artifact.id,
444
+ flashcards: artifact.flashcards,
445
+ };
446
+ }
447
+ }
@@ -1,4 +1,5 @@
1
1
  import { Prisma, type PrismaClient } from '@prisma/client';
2
+ import { BaseService } from './base.service.js';
2
3
  /**
3
4
  * SM-2 Spaced Repetition Algorithm
4
5
  * https://www.supermemo.com/en/archives1990-2015/english/ol/sm2
@@ -9,8 +10,7 @@ export interface SM2Result {
9
10
  repetitions: number;
10
11
  nextReviewAt: Date;
11
12
  }
12
- export declare class FlashcardProgressService {
13
- private db;
13
+ export declare class FlashcardProgressService extends BaseService {
14
14
  constructor(db: PrismaClient);
15
15
  /**
16
16
  * Calculate next review using SM-2 algorithm with smart scheduling
@@ -1,8 +1,9 @@
1
1
  import { Prisma } from '@prisma/client';
2
2
  import { NotFoundError } from '../lib/errors.js';
3
- export class FlashcardProgressService {
3
+ import { BaseService } from './base.service.js';
4
+ export class FlashcardProgressService extends BaseService {
4
5
  constructor(db) {
5
- this.db = db;
6
+ super(db);
6
7
  }
7
8
  /**
8
9
  * Calculate next review using SM-2 algorithm with smart scheduling