@studious-lms/server 1.14.0 → 1.16.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 (129) hide show
  1. package/dist/lib/inferDocumentFormat.d.ts +10 -0
  2. package/dist/lib/inferDocumentFormat.d.ts.map +1 -0
  3. package/dist/lib/inferDocumentFormat.js +48 -0
  4. package/dist/lib/inferDocumentFormat.js.map +1 -0
  5. package/dist/models/announcement.d.ts +45 -45
  6. package/dist/models/assignment.d.ts +75 -75
  7. package/dist/models/class.d.ts +13 -13
  8. package/dist/models/comment.d.ts +6 -6
  9. package/dist/models/conversation.d.ts +23 -23
  10. package/dist/models/event.d.ts +18 -18
  11. package/dist/models/file.d.ts +9 -9
  12. package/dist/models/labChat.d.ts +4 -4
  13. package/dist/models/message.d.ts +13 -13
  14. package/dist/models/newtonChat.d.ts +3 -3
  15. package/dist/models/notification.d.ts +4 -4
  16. package/dist/models/worksheet.d.ts +121 -121
  17. package/dist/pipelines/aiLabChat.d.ts +7 -1
  18. package/dist/pipelines/aiLabChat.d.ts.map +1 -1
  19. package/dist/pipelines/aiLabChat.js +145 -67
  20. package/dist/pipelines/aiLabChat.js.map +1 -1
  21. package/dist/pipelines/aiLabChatContract.d.ts +34 -195
  22. package/dist/pipelines/aiLabChatContract.d.ts.map +1 -1
  23. package/dist/pipelines/aiLabChatContract.js +9 -19
  24. package/dist/pipelines/aiLabChatContract.js.map +1 -1
  25. package/dist/pipelines/gradeWorksheet.d.ts +6 -6
  26. package/dist/pipelines/gradeWorksheet.d.ts.map +1 -1
  27. package/dist/pipelines/gradeWorksheet.js +75 -36
  28. package/dist/pipelines/gradeWorksheet.js.map +1 -1
  29. package/dist/pipelines/labChatPrompt.d.ts +8 -3
  30. package/dist/pipelines/labChatPrompt.d.ts.map +1 -1
  31. package/dist/pipelines/labChatPrompt.js +49 -14
  32. package/dist/pipelines/labChatPrompt.js.map +1 -1
  33. package/dist/pipelines/pdfToWorksheet.d.ts +1 -1
  34. package/dist/routers/_app.d.ts +524 -516
  35. package/dist/routers/_app.d.ts.map +1 -1
  36. package/dist/routers/announcement.d.ts +40 -40
  37. package/dist/routers/assignment.d.ts +121 -121
  38. package/dist/routers/class.d.ts +8 -8
  39. package/dist/routers/comment.d.ts +5 -5
  40. package/dist/routers/conversation.d.ts +6 -6
  41. package/dist/routers/discussion.d.ts +3 -3
  42. package/dist/routers/embedding.d.ts +6 -2
  43. package/dist/routers/embedding.d.ts.map +1 -1
  44. package/dist/routers/embedding.js +44 -36
  45. package/dist/routers/embedding.js.map +1 -1
  46. package/dist/routers/event.d.ts +18 -18
  47. package/dist/routers/file.d.ts +2 -2
  48. package/dist/routers/labChat.d.ts +3 -3
  49. package/dist/routers/notifications.d.ts +4 -4
  50. package/dist/routers/search.d.ts +2 -2
  51. package/dist/routers/worksheet.d.ts +44 -44
  52. package/dist/routes/aiStream.d.ts.map +1 -1
  53. package/dist/routes/aiStream.js +41 -11
  54. package/dist/routes/aiStream.js.map +1 -1
  55. package/dist/seedDatabase.d.ts +1 -1
  56. package/dist/services/aiChatStream.d.ts +18 -0
  57. package/dist/services/aiChatStream.d.ts.map +1 -1
  58. package/dist/services/aiChatStream.js +81 -11
  59. package/dist/services/aiChatStream.js.map +1 -1
  60. package/dist/services/aiModifierTools.d.ts +0 -16
  61. package/dist/services/aiModifierTools.d.ts.map +1 -1
  62. package/dist/services/aiModifierTools.js +112 -3
  63. package/dist/services/aiModifierTools.js.map +1 -1
  64. package/dist/services/aiProposalTools.d.ts.map +1 -1
  65. package/dist/services/aiProposalTools.js +25 -25
  66. package/dist/services/aiProposalTools.js.map +1 -1
  67. package/dist/services/announcement.d.ts +34 -34
  68. package/dist/services/assignment.d.ts +111 -111
  69. package/dist/services/assignment.d.ts.map +1 -1
  70. package/dist/services/assignment.js +4 -2
  71. package/dist/services/assignment.js.map +1 -1
  72. package/dist/services/class.d.ts +6 -6
  73. package/dist/services/class.d.ts.map +1 -1
  74. package/dist/services/class.js +7 -3
  75. package/dist/services/class.js.map +1 -1
  76. package/dist/services/comment.d.ts +5 -5
  77. package/dist/services/conversation.d.ts +6 -6
  78. package/dist/services/discussion.d.ts.map +1 -1
  79. package/dist/services/discussion.js +9 -2
  80. package/dist/services/discussion.js.map +1 -1
  81. package/dist/services/embedding.d.ts +19 -1
  82. package/dist/services/embedding.d.ts.map +1 -1
  83. package/dist/services/embedding.js +154 -8
  84. package/dist/services/embedding.js.map +1 -1
  85. package/dist/services/event.d.ts +18 -18
  86. package/dist/services/file.d.ts +4 -4
  87. package/dist/services/folder.d.ts.map +1 -1
  88. package/dist/services/folder.js +32 -2
  89. package/dist/services/folder.js.map +1 -1
  90. package/dist/services/labChat.d.ts +3 -3
  91. package/dist/services/notification.d.ts +4 -4
  92. package/dist/services/openaiDocumentGen.d.ts +25 -0
  93. package/dist/services/openaiDocumentGen.d.ts.map +1 -0
  94. package/dist/services/openaiDocumentGen.js +195 -0
  95. package/dist/services/openaiDocumentGen.js.map +1 -0
  96. package/dist/services/search.d.ts +1 -1
  97. package/dist/services/search.d.ts.map +1 -1
  98. package/dist/services/search.js +27 -2
  99. package/dist/services/search.js.map +1 -1
  100. package/dist/services/worksheet.d.ts +66 -49
  101. package/dist/services/worksheet.d.ts.map +1 -1
  102. package/dist/services/worksheet.js +48 -3
  103. package/dist/services/worksheet.js.map +1 -1
  104. package/dist/workers/processors/embedContent.d.ts.map +1 -1
  105. package/dist/workers/processors/embedContent.js +16 -25
  106. package/dist/workers/processors/embedContent.js.map +1 -1
  107. package/package.json +1 -1
  108. package/prisma/migrations/20260610140000_expand_embedding_source_types/migration.sql +4 -0
  109. package/prisma/schema.prisma +3 -0
  110. package/src/lib/inferDocumentFormat.ts +58 -0
  111. package/src/pipelines/aiLabChat.ts +185 -74
  112. package/src/pipelines/aiLabChatContract.ts +7 -17
  113. package/src/pipelines/gradeWorksheet.ts +95 -38
  114. package/src/pipelines/labChatPrompt.ts +61 -14
  115. package/src/routers/embedding.ts +44 -39
  116. package/src/routes/aiStream.ts +47 -9
  117. package/src/services/aiChatStream.ts +103 -8
  118. package/src/services/aiModifierTools.ts +154 -1
  119. package/src/services/aiProposalTools.ts +26 -25
  120. package/src/services/assignment.ts +3 -0
  121. package/src/services/class.ts +7 -1
  122. package/src/services/discussion.ts +13 -0
  123. package/src/services/embedding.ts +167 -6
  124. package/src/services/folder.ts +31 -0
  125. package/src/services/openaiDocumentGen.ts +237 -0
  126. package/src/services/search.ts +25 -1
  127. package/src/services/worksheet.ts +68 -1
  128. package/src/workers/processors/embedContent.ts +18 -22
  129. package/tests/unit/lib/inferDocumentFormat.test.ts +33 -0
@@ -47,11 +47,11 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
47
47
  id: string;
48
48
  createdAt: Date;
49
49
  updatedAt: Date;
50
- order: number | null;
51
- worksheetId: string;
50
+ points: number;
52
51
  question: string;
52
+ worksheetId: string;
53
+ order: number | null;
53
54
  answer: string;
54
- points: number;
55
55
  }[];
56
56
  } & {
57
57
  status: import(".prisma/client").$Enums.JobStatus | null;
@@ -60,8 +60,8 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
60
60
  updatedAt: Date;
61
61
  name: string;
62
62
  classId: string;
63
- sourceUrl: string | null;
64
63
  runJobId: string | null;
64
+ sourceUrl: string | null;
65
65
  };
66
66
  meta: object;
67
67
  }>;
@@ -96,8 +96,8 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
96
96
  updatedAt: Date;
97
97
  name: string;
98
98
  classId: string;
99
- sourceUrl: string | null;
100
99
  runJobId: string | null;
100
+ sourceUrl: string | null;
101
101
  }[];
102
102
  meta: object;
103
103
  }>;
@@ -113,8 +113,8 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
113
113
  updatedAt: Date;
114
114
  name: string;
115
115
  classId: string;
116
- sourceUrl: string | null;
117
116
  runJobId: string | null;
117
+ sourceUrl: string | null;
118
118
  };
119
119
  meta: object;
120
120
  }>;
@@ -129,8 +129,8 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
129
129
  updatedAt: Date;
130
130
  name: string;
131
131
  classId: string;
132
- sourceUrl: string | null;
133
132
  runJobId: string | null;
133
+ sourceUrl: string | null;
134
134
  };
135
135
  meta: object;
136
136
  }>;
@@ -147,8 +147,8 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
147
147
  updatedAt: Date;
148
148
  name: string;
149
149
  classId: string;
150
- sourceUrl: string | null;
151
150
  runJobId: string | null;
151
+ sourceUrl: string | null;
152
152
  };
153
153
  meta: object;
154
154
  }>;
@@ -166,8 +166,8 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
166
166
  updatedAt: Date;
167
167
  name: string;
168
168
  classId: string;
169
- sourceUrl: string | null;
170
169
  runJobId: string | null;
170
+ sourceUrl: string | null;
171
171
  };
172
172
  meta: object;
173
173
  }>;
@@ -242,13 +242,13 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
242
242
  addQuestion: import("@trpc/server").TRPCMutationProcedure<{
243
243
  input: {
244
244
  type: "ESSAY" | "MULTIPLE_CHOICE" | "TRUE_FALSE" | "SHORT_ANSWER" | "LONG_ANSWER" | "MATH_EXPRESSION";
245
- worksheetId: string;
246
245
  question: string;
246
+ worksheetId: string;
247
247
  answer: string;
248
248
  options?: any;
249
249
  markScheme?: any;
250
- order?: number | undefined;
251
250
  points?: number | undefined;
251
+ order?: number | undefined;
252
252
  };
253
253
  output: {
254
254
  type: import(".prisma/client").$Enums.WorksheetQuestionType;
@@ -257,11 +257,11 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
257
257
  id: string;
258
258
  createdAt: Date;
259
259
  updatedAt: Date;
260
- order: number | null;
261
- worksheetId: string;
260
+ points: number;
262
261
  question: string;
262
+ worksheetId: string;
263
+ order: number | null;
263
264
  answer: string;
264
- points: number;
265
265
  };
266
266
  meta: object;
267
267
  }>;
@@ -279,14 +279,14 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
279
279
  }>;
280
280
  updateQuestion: import("@trpc/server").TRPCMutationProcedure<{
281
281
  input: {
282
- worksheetId: string;
283
282
  questionId: string;
283
+ worksheetId: string;
284
284
  type?: "ESSAY" | "MULTIPLE_CHOICE" | "TRUE_FALSE" | "SHORT_ANSWER" | "LONG_ANSWER" | "MATH_EXPRESSION" | undefined;
285
285
  options?: any;
286
286
  markScheme?: any;
287
+ points?: number | undefined;
287
288
  question?: string | undefined;
288
289
  answer?: string | undefined;
289
- points?: number | undefined;
290
290
  };
291
291
  output: {
292
292
  type: import(".prisma/client").$Enums.WorksheetQuestionType;
@@ -295,18 +295,18 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
295
295
  id: string;
296
296
  createdAt: Date;
297
297
  updatedAt: Date;
298
- order: number | null;
299
- worksheetId: string;
298
+ points: number;
300
299
  question: string;
300
+ worksheetId: string;
301
+ order: number | null;
301
302
  answer: string;
302
- points: number;
303
303
  };
304
304
  meta: object;
305
305
  }>;
306
306
  deleteQuestion: import("@trpc/server").TRPCMutationProcedure<{
307
307
  input: {
308
- worksheetId: string;
309
308
  questionId: string;
309
+ worksheetId: string;
310
310
  };
311
311
  output: {
312
312
  type: import(".prisma/client").$Enums.WorksheetQuestionType;
@@ -315,11 +315,11 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
315
315
  id: string;
316
316
  createdAt: Date;
317
317
  updatedAt: Date;
318
- order: number | null;
319
- worksheetId: string;
318
+ points: number;
320
319
  question: string;
320
+ worksheetId: string;
321
+ order: number | null;
321
322
  answer: string;
322
- points: number;
323
323
  };
324
324
  meta: object;
325
325
  }>;
@@ -340,14 +340,14 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
340
340
  createdAt: Date;
341
341
  updatedAt: Date | null;
342
342
  feedback: string | null;
343
- studentId: string;
344
343
  runJobId: string | null;
345
- points: number;
346
344
  response: string;
347
345
  isCorrect: boolean;
348
346
  markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
349
- studentWorksheetResponseId: string | null;
347
+ points: number;
348
+ studentId: string;
350
349
  questionId: string;
350
+ studentWorksheetResponseId: string | null;
351
351
  })[];
352
352
  } & {
353
353
  id: string;
@@ -355,9 +355,9 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
355
355
  updatedAt: Date;
356
356
  submissionId: string | null;
357
357
  studentId: string;
358
+ worksheetId: string;
358
359
  submittedAt: Date | null;
359
360
  submitted: boolean;
360
- worksheetId: string;
361
361
  };
362
362
  meta: object;
363
363
  }>;
@@ -374,14 +374,14 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
374
374
  createdAt: Date;
375
375
  updatedAt: Date | null;
376
376
  feedback: string | null;
377
- studentId: string;
378
377
  runJobId: string | null;
379
- points: number;
380
378
  response: string;
381
379
  isCorrect: boolean;
382
380
  markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
383
- studentWorksheetResponseId: string | null;
381
+ points: number;
382
+ studentId: string;
384
383
  questionId: string;
384
+ studentWorksheetResponseId: string | null;
385
385
  }[];
386
386
  } & {
387
387
  id: string;
@@ -389,9 +389,9 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
389
389
  updatedAt: Date;
390
390
  submissionId: string | null;
391
391
  studentId: string;
392
+ worksheetId: string;
392
393
  submittedAt: Date | null;
393
394
  submitted: boolean;
394
- worksheetId: string;
395
395
  };
396
396
  meta: object;
397
397
  }>;
@@ -406,14 +406,14 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
406
406
  createdAt: Date;
407
407
  updatedAt: Date | null;
408
408
  feedback: string | null;
409
- studentId: string;
410
409
  runJobId: string | null;
411
- points: number;
412
410
  response: string;
413
411
  isCorrect: boolean;
414
412
  markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
415
- studentWorksheetResponseId: string | null;
413
+ points: number;
414
+ studentId: string;
416
415
  questionId: string;
416
+ studentWorksheetResponseId: string | null;
417
417
  };
418
418
  meta: object;
419
419
  }>;
@@ -428,26 +428,26 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
428
428
  createdAt: Date;
429
429
  updatedAt: Date | null;
430
430
  feedback: string | null;
431
- studentId: string;
432
431
  runJobId: string | null;
433
- points: number;
434
432
  response: string;
435
433
  isCorrect: boolean;
436
434
  markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
437
- studentWorksheetResponseId: string | null;
435
+ points: number;
436
+ studentId: string;
438
437
  questionId: string;
438
+ studentWorksheetResponseId: string | null;
439
439
  } | null;
440
440
  meta: object;
441
441
  }>;
442
442
  gradeAnswer: import("@trpc/server").TRPCMutationProcedure<{
443
443
  input: {
444
444
  isCorrect: boolean;
445
- studentWorksheetResponseId: string;
446
445
  questionId: string;
446
+ studentWorksheetResponseId: string;
447
447
  feedback?: string | undefined;
448
- points?: number | undefined;
449
448
  response?: string | undefined;
450
449
  markschemeState?: any;
450
+ points?: number | undefined;
451
451
  responseId?: string | undefined;
452
452
  };
453
453
  output: {
@@ -456,14 +456,14 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
456
456
  createdAt: Date;
457
457
  updatedAt: Date | null;
458
458
  feedback: string | null;
459
- studentId: string;
460
459
  runJobId: string | null;
461
- points: number;
462
460
  response: string;
463
461
  isCorrect: boolean;
464
462
  markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
465
- studentWorksheetResponseId: string | null;
463
+ points: number;
464
+ studentId: string;
466
465
  questionId: string;
466
+ studentWorksheetResponseId: string | null;
467
467
  };
468
468
  meta: object;
469
469
  }>;
@@ -478,8 +478,8 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
478
478
  content: string;
479
479
  assignmentId: string | null;
480
480
  submissionId: string | null;
481
- modifiedAt: Date | null;
482
481
  announcementId: string | null;
482
+ modifiedAt: Date | null;
483
483
  authorId: string;
484
484
  isPinned: boolean;
485
485
  parentCommentId: string | null;
@@ -519,8 +519,8 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
519
519
  updatedAt: Date;
520
520
  name: string;
521
521
  classId: string;
522
- sourceUrl: string | null;
523
522
  runJobId: string | null;
523
+ sourceUrl: string | null;
524
524
  };
525
525
  meta: object;
526
526
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"aiStream.d.ts","sourceRoot":"/","sources":["routes/aiStream.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,cAAc,4CAAW,CAAC"}
1
+ {"version":3,"file":"aiStream.d.ts","sourceRoot":"/","sources":["routes/aiStream.ts"],"names":[],"mappings":"AA4BA,eAAO,MAAM,cAAc,4CAAW,CAAC"}
@@ -3,7 +3,7 @@
3
3
  * These routes use the Vercel AI SDK's data stream protocol for streaming AI responses.
4
4
  */
5
5
 
6
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="de32fb67-392b-5976-8b7c-1098752de11a")}catch(e){}}();
6
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="14ec689a-63a6-514d-9ced-078103a2eb8e")}catch(e){}}();
7
7
  import { Router } from 'express';
8
8
  import { streamText, stepCountIs } from 'ai';
9
9
  import { openai } from '@ai-sdk/openai';
@@ -17,7 +17,7 @@ import { pusher, chatChannel, teacherChannel } from '../lib/pusher.js';
17
17
  import { getAIUserId, ensureAIUserExists, isAIUser } from '../utils/aiUser.js';
18
18
  import { JobStatus } from '@prisma/client';
19
19
  import { updateAssignmentRecord } from '../services/assignment.js';
20
- import { updateWorksheetRecord } from '../services/worksheet.js';
20
+ import { restoreWorksheetQuestions, updateWorksheetRecord, } from '../services/worksheet.js';
21
21
  export const aiStreamRouter = Router();
22
22
  /**
23
23
  * Authenticate request from session token (header or cookie).
@@ -92,7 +92,7 @@ aiStreamRouter.post('/chat/stream', async (req, res) => {
92
92
  if (!classId) {
93
93
  return res.status(400).json({ error: 'Unable to determine class context' });
94
94
  }
95
- // Build message history from DB
95
+ // Build message history from DB (include attachments so the LLM can see them)
96
96
  const recentMessages = await prisma.message.findMany({
97
97
  where: { conversationId, status: { not: JobStatus.PENDING } },
98
98
  orderBy: { createdAt: 'asc' },
@@ -101,14 +101,22 @@ aiStreamRouter.post('/chat/stream', async (req, res) => {
101
101
  content: true,
102
102
  senderId: true,
103
103
  status: true,
104
+ attachments: {
105
+ select: { id: true, name: true, type: true, path: true },
106
+ },
104
107
  },
105
108
  });
106
109
  const messages = recentMessages
107
110
  .filter((m) => m.content && m.content.length > 0)
108
- .map((m) => ({
109
- role: isAIUser(m.senderId) ? 'assistant' : 'user',
110
- content: m.content,
111
- }));
111
+ .map((m) => {
112
+ const role = isAIUser(m.senderId) ? 'assistant' : 'user';
113
+ if (role === 'assistant' || m.attachments.length === 0) {
114
+ return { role, content: m.content };
115
+ }
116
+ // Append file descriptions to user messages
117
+ const fileList = m.attachments.map((a) => `${a.name} (${a.type})`).join(', ');
118
+ return { role, content: `${m.content}\n[Attached files: ${fileList}]` };
119
+ });
112
120
  // Build system prompt based on context type
113
121
  let systemPrompt;
114
122
  if (labChat) {
@@ -331,6 +339,16 @@ aiStreamRouter.post('/modify/stream', async (req, res) => {
331
339
  resourceContext = buildWorksheetContext(worksheet);
332
340
  preEditSnapshot = {
333
341
  name: worksheet.name,
342
+ questions: worksheet.questions.map((q) => ({
343
+ id: q.id,
344
+ type: q.type,
345
+ question: q.question,
346
+ answer: q.answer,
347
+ options: q.options,
348
+ markScheme: q.markScheme,
349
+ points: q.points,
350
+ order: q.order,
351
+ })),
334
352
  };
335
353
  }
336
354
  const systemPrompt = buildModifierSystemPrompt(assignmentId ? 'assignment' : 'worksheet');
@@ -412,7 +430,7 @@ aiStreamRouter.post('/modify/stream', async (req, res) => {
412
430
  tools,
413
431
  temperature: 0.3,
414
432
  maxOutputTokens: 8192,
415
- stopWhen: stepCountIs(8),
433
+ stopWhen: stepCountIs(16),
416
434
  });
417
435
  // Drain the stream. Field writes happen inside the tools' execute (which
418
436
  // call onFieldUpdate); any narration text is forwarded to the SSE client.
@@ -532,7 +550,11 @@ aiStreamRouter.post('/modify/undo', async (req, res) => {
532
550
  });
533
551
  }
534
552
  else {
535
- await updateWorksheetRecord(resourceId, user.id, parsed.snapshot);
553
+ const { questions, ...fields } = parsed.snapshot;
554
+ await updateWorksheetRecord(resourceId, user.id, fields);
555
+ if (Array.isArray(questions)) {
556
+ await restoreWorksheetQuestions(resourceId, user.id, questions);
557
+ }
536
558
  }
537
559
  // Remove the snapshot so undo can only be used once
538
560
  await redis.del(snapshotKey);
@@ -618,14 +640,21 @@ RULES:
618
640
 
619
641
  HOW TO EDIT: You change the worksheet by CALLING TOOLS — never by printing the new value as text.
620
642
  - Call \`update_worksheet_field\` for the field the teacher wants changed (currently: name).
643
+ - Call \`add_worksheet_question\` once per new question. Question types:
644
+ MULTIPLE_CHOICE|TRUE_FALSE|SHORT_ANSWER|LONG_ANSWER|MATH_EXPRESSION|ESSAY.
645
+ For MULTIPLE_CHOICE provide \`options\` with exactly one \`isCorrect: true\`.
646
+ - Call \`update_worksheet_question\` / \`delete_worksheet_question\` with the question's
647
+ Q_ID (shown in the worksheet context) to edit or remove existing questions.
621
648
  - Use \`search_materials\` first only if you need class context.
622
649
 
623
650
  RULES:
624
651
  - Only change what the teacher asked about.
625
652
  - Each tool call saves immediately and is shown to the teacher live.
653
+ - Write real, pedagogically sound question content with correct answers — never placeholders.
626
654
  - After making all the changes, write a brief conversational summary of what you did.
627
655
  Be human and specific — mention the actual values. For example:
628
- "Done — I've renamed the worksheet to 'Unit 3 Practice Problems'."
656
+ "Done — I've renamed the worksheet to 'Unit 3 Practice Problems' and added two
657
+ questions on volumes of revolution."
629
658
  Do NOT dump the full worksheet contents — just summarize naturally.`;
630
659
  }
631
660
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -677,6 +706,7 @@ ACCEPT_WORKSHEET: ${assignment.acceptWorksheet}`;
677
706
  function buildWorksheetContext(worksheet) {
678
707
  let context = `WORKSHEET_NAME: ${worksheet.name}\nQUESTIONS_START`;
679
708
  for (const q of worksheet.questions) {
709
+ context += `\nQ_ID: ${q.id}`;
680
710
  context += `\nQ_TYPE: ${q.type}`;
681
711
  context += `\nQ_TEXT: ${q.question}`;
682
712
  context += `\nQ_ANSWER: ${q.answer}`;
@@ -693,4 +723,4 @@ function buildWorksheetContext(worksheet) {
693
723
  return context;
694
724
  }
695
725
  //# sourceMappingURL=aiStream.js.map
696
- //# debugId=de32fb67-392b-5976-8b7c-1098752de11a
726
+ //# debugId=14ec689a-63a6-514d-9ced-078103a2eb8e