lingxios 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (298) hide show
  1. package/README.md +54 -0
  2. package/db/schema.sql +306 -0
  3. package/dist/src/app/artifacts.d.ts +8 -0
  4. package/dist/src/app/artifacts.js +114 -0
  5. package/dist/src/app/artifacts.js.map +1 -0
  6. package/dist/src/app/doctor.d.ts +22 -0
  7. package/dist/src/app/doctor.js +44 -0
  8. package/dist/src/app/doctor.js.map +1 -0
  9. package/dist/src/app/index.d.ts +123 -0
  10. package/dist/src/app/index.js +291 -0
  11. package/dist/src/app/index.js.map +1 -0
  12. package/dist/src/app/input.d.ts +22 -0
  13. package/dist/src/app/input.js +60 -0
  14. package/dist/src/app/input.js.map +1 -0
  15. package/dist/src/app/recover-wait.d.ts +5 -0
  16. package/dist/src/app/recover-wait.js +59 -0
  17. package/dist/src/app/recover-wait.js.map +1 -0
  18. package/dist/src/app/resources.d.ts +5 -0
  19. package/dist/src/app/resources.js +9 -0
  20. package/dist/src/app/resources.js.map +1 -0
  21. package/dist/src/app/storage.d.ts +2 -0
  22. package/dist/src/app/storage.js +41 -0
  23. package/dist/src/app/storage.js.map +1 -0
  24. package/dist/src/cli/doctor.d.ts +2 -0
  25. package/dist/src/cli/doctor.js +20 -0
  26. package/dist/src/cli/doctor.js.map +1 -0
  27. package/dist/src/cli/eval.d.ts +2 -0
  28. package/dist/src/cli/eval.js +44 -0
  29. package/dist/src/cli/eval.js.map +1 -0
  30. package/dist/src/config.d.ts +22 -0
  31. package/dist/src/config.js +56 -0
  32. package/dist/src/config.js.map +1 -0
  33. package/dist/src/context/attachments.d.ts +10 -0
  34. package/dist/src/context/attachments.js +18 -0
  35. package/dist/src/context/attachments.js.map +1 -0
  36. package/dist/src/context/document-text.d.ts +1 -0
  37. package/dist/src/context/document-text.js +29 -0
  38. package/dist/src/context/document-text.js.map +1 -0
  39. package/dist/src/context/document-worker.d.ts +1 -0
  40. package/dist/src/context/document-worker.js +62 -0
  41. package/dist/src/context/document-worker.js.map +1 -0
  42. package/dist/src/context/evidence.d.ts +19 -0
  43. package/dist/src/context/evidence.js +37 -0
  44. package/dist/src/context/evidence.js.map +1 -0
  45. package/dist/src/context/request.d.ts +21 -0
  46. package/dist/src/context/request.js +32 -0
  47. package/dist/src/context/request.js.map +1 -0
  48. package/dist/src/context/research-evidence.d.ts +4 -0
  49. package/dist/src/context/research-evidence.js +24 -0
  50. package/dist/src/context/research-evidence.js.map +1 -0
  51. package/dist/src/context/resource-checks.d.ts +7 -0
  52. package/dist/src/context/resource-checks.js +15 -0
  53. package/dist/src/context/resource-checks.js.map +1 -0
  54. package/dist/src/context/task-contract.d.ts +11 -0
  55. package/dist/src/context/task-contract.js +23 -0
  56. package/dist/src/context/task-contract.js.map +1 -0
  57. package/dist/src/context/text.d.ts +2 -0
  58. package/dist/src/context/text.js +7 -0
  59. package/dist/src/context/text.js.map +1 -0
  60. package/dist/src/control-plane/http-server.d.ts +22 -0
  61. package/dist/src/control-plane/http-server.js +196 -0
  62. package/dist/src/control-plane/http-server.js.map +1 -0
  63. package/dist/src/control-plane/memory-store.d.ts +71 -0
  64. package/dist/src/control-plane/memory-store.js +389 -0
  65. package/dist/src/control-plane/memory-store.js.map +1 -0
  66. package/dist/src/control-plane/pg-store.d.ts +62 -0
  67. package/dist/src/control-plane/pg-store.js +386 -0
  68. package/dist/src/control-plane/pg-store.js.map +1 -0
  69. package/dist/src/control-plane/service.d.ts +52 -0
  70. package/dist/src/control-plane/service.js +630 -0
  71. package/dist/src/control-plane/service.js.map +1 -0
  72. package/dist/src/control-plane/stores.d.ts +184 -0
  73. package/dist/src/control-plane/stores.js +19 -0
  74. package/dist/src/control-plane/stores.js.map +1 -0
  75. package/dist/src/errors.d.ts +66 -0
  76. package/dist/src/errors.js +102 -0
  77. package/dist/src/errors.js.map +1 -0
  78. package/dist/src/eval/calibration.d.ts +16 -0
  79. package/dist/src/eval/calibration.js +28 -0
  80. package/dist/src/eval/calibration.js.map +1 -0
  81. package/dist/src/eval/index.d.ts +40 -0
  82. package/dist/src/eval/index.js +40 -0
  83. package/dist/src/eval/index.js.map +1 -0
  84. package/dist/src/eval/review.d.ts +20 -0
  85. package/dist/src/eval/review.js +49 -0
  86. package/dist/src/eval/review.js.map +1 -0
  87. package/dist/src/host/http-client.d.ts +48 -0
  88. package/dist/src/host/http-client.js +127 -0
  89. package/dist/src/host/http-client.js.map +1 -0
  90. package/dist/src/host/port.d.ts +26 -0
  91. package/dist/src/host/port.js +2 -0
  92. package/dist/src/host/port.js.map +1 -0
  93. package/dist/src/index.d.ts +17 -0
  94. package/dist/src/index.js +9 -0
  95. package/dist/src/index.js.map +1 -0
  96. package/dist/src/integrations/lingxiloop/actions.d.ts +25 -0
  97. package/dist/src/integrations/lingxiloop/actions.js +77 -0
  98. package/dist/src/integrations/lingxiloop/actions.js.map +1 -0
  99. package/dist/src/integrations/lingxiloop/app.d.ts +167 -0
  100. package/dist/src/integrations/lingxiloop/app.js +434 -0
  101. package/dist/src/integrations/lingxiloop/app.js.map +1 -0
  102. package/dist/src/integrations/lingxiloop/approvals.d.ts +148 -0
  103. package/dist/src/integrations/lingxiloop/approvals.js +540 -0
  104. package/dist/src/integrations/lingxiloop/approvals.js.map +1 -0
  105. package/dist/src/integrations/lingxiloop/attachments.d.ts +8 -0
  106. package/dist/src/integrations/lingxiloop/attachments.js +38 -0
  107. package/dist/src/integrations/lingxiloop/attachments.js.map +1 -0
  108. package/dist/src/integrations/lingxiloop/calendar-approvals.d.ts +21 -0
  109. package/dist/src/integrations/lingxiloop/calendar-approvals.js +126 -0
  110. package/dist/src/integrations/lingxiloop/calendar-approvals.js.map +1 -0
  111. package/dist/src/integrations/lingxiloop/calendar-writes.d.ts +15 -0
  112. package/dist/src/integrations/lingxiloop/calendar-writes.js +79 -0
  113. package/dist/src/integrations/lingxiloop/calendar-writes.js.map +1 -0
  114. package/dist/src/integrations/lingxiloop/calendar.d.ts +19 -0
  115. package/dist/src/integrations/lingxiloop/calendar.js +43 -0
  116. package/dist/src/integrations/lingxiloop/calendar.js.map +1 -0
  117. package/dist/src/integrations/lingxiloop/canvas-events.d.ts +7 -0
  118. package/dist/src/integrations/lingxiloop/canvas-events.js +53 -0
  119. package/dist/src/integrations/lingxiloop/canvas-events.js.map +1 -0
  120. package/dist/src/integrations/lingxiloop/canvas-summary.d.ts +5 -0
  121. package/dist/src/integrations/lingxiloop/canvas-summary.js +52 -0
  122. package/dist/src/integrations/lingxiloop/canvas-summary.js.map +1 -0
  123. package/dist/src/integrations/lingxiloop/canvas-work-report.d.ts +12 -0
  124. package/dist/src/integrations/lingxiloop/canvas-work-report.js +120 -0
  125. package/dist/src/integrations/lingxiloop/canvas-work-report.js.map +1 -0
  126. package/dist/src/integrations/lingxiloop/canvas-work.d.ts +11 -0
  127. package/dist/src/integrations/lingxiloop/canvas-work.js +306 -0
  128. package/dist/src/integrations/lingxiloop/canvas-work.js.map +1 -0
  129. package/dist/src/integrations/lingxiloop/canvas.d.ts +12 -0
  130. package/dist/src/integrations/lingxiloop/canvas.js +79 -0
  131. package/dist/src/integrations/lingxiloop/canvas.js.map +1 -0
  132. package/dist/src/integrations/lingxiloop/chat.d.ts +11 -0
  133. package/dist/src/integrations/lingxiloop/chat.js +46 -0
  134. package/dist/src/integrations/lingxiloop/chat.js.map +1 -0
  135. package/dist/src/integrations/lingxiloop/document-approvals.d.ts +21 -0
  136. package/dist/src/integrations/lingxiloop/document-approvals.js +75 -0
  137. package/dist/src/integrations/lingxiloop/document-approvals.js.map +1 -0
  138. package/dist/src/integrations/lingxiloop/document-content.d.ts +80 -0
  139. package/dist/src/integrations/lingxiloop/document-content.js +195 -0
  140. package/dist/src/integrations/lingxiloop/document-content.js.map +1 -0
  141. package/dist/src/integrations/lingxiloop/document-writes.d.ts +8 -0
  142. package/dist/src/integrations/lingxiloop/document-writes.js +49 -0
  143. package/dist/src/integrations/lingxiloop/document-writes.js.map +1 -0
  144. package/dist/src/integrations/lingxiloop/documents.d.ts +22 -0
  145. package/dist/src/integrations/lingxiloop/documents.js +38 -0
  146. package/dist/src/integrations/lingxiloop/documents.js.map +1 -0
  147. package/dist/src/integrations/lingxiloop/event-outbox.d.ts +5 -0
  148. package/dist/src/integrations/lingxiloop/event-outbox.js +47 -0
  149. package/dist/src/integrations/lingxiloop/event-outbox.js.map +1 -0
  150. package/dist/src/integrations/lingxiloop/index.d.ts +3 -0
  151. package/dist/src/integrations/lingxiloop/index.js +3 -0
  152. package/dist/src/integrations/lingxiloop/index.js.map +1 -0
  153. package/dist/src/integrations/lingxiloop/learning-attempts.d.ts +13 -0
  154. package/dist/src/integrations/lingxiloop/learning-attempts.js +58 -0
  155. package/dist/src/integrations/lingxiloop/learning-attempts.js.map +1 -0
  156. package/dist/src/integrations/lingxiloop/learning-evaluation.d.ts +8 -0
  157. package/dist/src/integrations/lingxiloop/learning-evaluation.js +64 -0
  158. package/dist/src/integrations/lingxiloop/learning-evaluation.js.map +1 -0
  159. package/dist/src/integrations/lingxiloop/learning-evidence.d.ts +7 -0
  160. package/dist/src/integrations/lingxiloop/learning-evidence.js +88 -0
  161. package/dist/src/integrations/lingxiloop/learning-evidence.js.map +1 -0
  162. package/dist/src/integrations/lingxiloop/learning-missions.d.ts +18 -0
  163. package/dist/src/integrations/lingxiloop/learning-missions.js +95 -0
  164. package/dist/src/integrations/lingxiloop/learning-missions.js.map +1 -0
  165. package/dist/src/integrations/lingxiloop/learning.d.ts +22 -0
  166. package/dist/src/integrations/lingxiloop/learning.js +150 -0
  167. package/dist/src/integrations/lingxiloop/learning.js.map +1 -0
  168. package/dist/src/integrations/lingxiloop/memory.d.ts +14 -0
  169. package/dist/src/integrations/lingxiloop/memory.js +133 -0
  170. package/dist/src/integrations/lingxiloop/memory.js.map +1 -0
  171. package/dist/src/integrations/lingxiloop/polls.d.ts +9 -0
  172. package/dist/src/integrations/lingxiloop/polls.js +47 -0
  173. package/dist/src/integrations/lingxiloop/polls.js.map +1 -0
  174. package/dist/src/integrations/lingxiloop/presentations.d.ts +11 -0
  175. package/dist/src/integrations/lingxiloop/presentations.js +34 -0
  176. package/dist/src/integrations/lingxiloop/presentations.js.map +1 -0
  177. package/dist/src/integrations/lingxiloop/questionnaire.d.ts +21 -0
  178. package/dist/src/integrations/lingxiloop/questionnaire.js +57 -0
  179. package/dist/src/integrations/lingxiloop/questionnaire.js.map +1 -0
  180. package/dist/src/integrations/lingxiloop/research.d.ts +19 -0
  181. package/dist/src/integrations/lingxiloop/research.js +20 -0
  182. package/dist/src/integrations/lingxiloop/research.js.map +1 -0
  183. package/dist/src/integrations/lingxiloop/routines.d.ts +91 -0
  184. package/dist/src/integrations/lingxiloop/routines.js +198 -0
  185. package/dist/src/integrations/lingxiloop/routines.js.map +1 -0
  186. package/dist/src/integrations/lingxiloop/service-contracts.d.ts +1045 -0
  187. package/dist/src/integrations/lingxiloop/service-contracts.js +2 -0
  188. package/dist/src/integrations/lingxiloop/service-contracts.js.map +1 -0
  189. package/dist/src/integrations/lingxiloop/teacher-context.d.ts +32 -0
  190. package/dist/src/integrations/lingxiloop/teacher-context.js +25 -0
  191. package/dist/src/integrations/lingxiloop/teacher-context.js.map +1 -0
  192. package/dist/src/integrations/lingxiloop/teacher-digest.d.ts +13 -0
  193. package/dist/src/integrations/lingxiloop/teacher-digest.js +152 -0
  194. package/dist/src/integrations/lingxiloop/teacher-digest.js.map +1 -0
  195. package/dist/src/integrations/lingxiloop/teacher-preview.d.ts +22 -0
  196. package/dist/src/integrations/lingxiloop/teacher-preview.js +41 -0
  197. package/dist/src/integrations/lingxiloop/teacher-preview.js.map +1 -0
  198. package/dist/src/integrations/lingxiloop/teacher.d.ts +24 -0
  199. package/dist/src/integrations/lingxiloop/teacher.js +224 -0
  200. package/dist/src/integrations/lingxiloop/teacher.js.map +1 -0
  201. package/dist/src/kernel/manager.d.ts +52 -0
  202. package/dist/src/kernel/manager.js +407 -0
  203. package/dist/src/kernel/manager.js.map +1 -0
  204. package/dist/src/logging.d.ts +21 -0
  205. package/dist/src/logging.js +54 -0
  206. package/dist/src/logging.js.map +1 -0
  207. package/dist/src/memory/evidence.d.ts +6 -0
  208. package/dist/src/memory/evidence.js +43 -0
  209. package/dist/src/memory/evidence.js.map +1 -0
  210. package/dist/src/memory/processor.d.ts +3 -0
  211. package/dist/src/memory/processor.js +66 -0
  212. package/dist/src/memory/processor.js.map +1 -0
  213. package/dist/src/memory/semantic.d.ts +19 -0
  214. package/dist/src/memory/semantic.js +93 -0
  215. package/dist/src/memory/semantic.js.map +1 -0
  216. package/dist/src/memory/store.d.ts +20 -0
  217. package/dist/src/memory/store.js +34 -0
  218. package/dist/src/memory/store.js.map +1 -0
  219. package/dist/src/memory/synthesis.d.ts +24 -0
  220. package/dist/src/memory/synthesis.js +138 -0
  221. package/dist/src/memory/synthesis.js.map +1 -0
  222. package/dist/src/metrics.d.ts +43 -0
  223. package/dist/src/metrics.js +130 -0
  224. package/dist/src/metrics.js.map +1 -0
  225. package/dist/src/model/driver.d.ts +60 -0
  226. package/dist/src/model/driver.js +2 -0
  227. package/dist/src/model/driver.js.map +1 -0
  228. package/dist/src/model/embeddings.d.ts +24 -0
  229. package/dist/src/model/embeddings.js +97 -0
  230. package/dist/src/model/embeddings.js.map +1 -0
  231. package/dist/src/model/openai.d.ts +45 -0
  232. package/dist/src/model/openai.js +340 -0
  233. package/dist/src/model/openai.js.map +1 -0
  234. package/dist/src/outcome/assessment.d.ts +16 -0
  235. package/dist/src/outcome/assessment.js +45 -0
  236. package/dist/src/outcome/assessment.js.map +1 -0
  237. package/dist/src/outcome/content-check.d.ts +19 -0
  238. package/dist/src/outcome/content-check.js +44 -0
  239. package/dist/src/outcome/content-check.js.map +1 -0
  240. package/dist/src/outcome/envelope.d.ts +34 -0
  241. package/dist/src/outcome/envelope.js +42 -0
  242. package/dist/src/outcome/envelope.js.map +1 -0
  243. package/dist/src/protocol/constants.d.ts +25 -0
  244. package/dist/src/protocol/constants.js +26 -0
  245. package/dist/src/protocol/constants.js.map +1 -0
  246. package/dist/src/protocol/kernel-wire.d.ts +74 -0
  247. package/dist/src/protocol/kernel-wire.js +2 -0
  248. package/dist/src/protocol/kernel-wire.js.map +1 -0
  249. package/dist/src/protocol/outcome.d.ts +16 -0
  250. package/dist/src/protocol/outcome.js +14 -0
  251. package/dist/src/protocol/outcome.js.map +1 -0
  252. package/dist/src/protocol/types.d.ts +266 -0
  253. package/dist/src/protocol/types.js +19 -0
  254. package/dist/src/protocol/types.js.map +1 -0
  255. package/dist/src/research/address.d.ts +2 -0
  256. package/dist/src/research/address.js +32 -0
  257. package/dist/src/research/address.js.map +1 -0
  258. package/dist/src/research/fetch.d.ts +7 -0
  259. package/dist/src/research/fetch.js +83 -0
  260. package/dist/src/research/fetch.js.map +1 -0
  261. package/dist/src/research/index.d.ts +26 -0
  262. package/dist/src/research/index.js +109 -0
  263. package/dist/src/research/index.js.map +1 -0
  264. package/dist/src/runtime/compaction.d.ts +41 -0
  265. package/dist/src/runtime/compaction.js +86 -0
  266. package/dist/src/runtime/compaction.js.map +1 -0
  267. package/dist/src/runtime/corrections.d.ts +20 -0
  268. package/dist/src/runtime/corrections.js +27 -0
  269. package/dist/src/runtime/corrections.js.map +1 -0
  270. package/dist/src/runtime/policy.d.ts +42 -0
  271. package/dist/src/runtime/policy.js +91 -0
  272. package/dist/src/runtime/policy.js.map +1 -0
  273. package/dist/src/runtime/resource-refresh.d.ts +8 -0
  274. package/dist/src/runtime/resource-refresh.js +37 -0
  275. package/dist/src/runtime/resource-refresh.js.map +1 -0
  276. package/dist/src/runtime/runtime.d.ts +51 -0
  277. package/dist/src/runtime/runtime.js +804 -0
  278. package/dist/src/runtime/runtime.js.map +1 -0
  279. package/dist/src/runtime/tool.d.ts +13 -0
  280. package/dist/src/runtime/tool.js +42 -0
  281. package/dist/src/runtime/tool.js.map +1 -0
  282. package/dist/src/ui/index.d.ts +27 -0
  283. package/dist/src/ui/index.js +54 -0
  284. package/dist/src/ui/index.js.map +1 -0
  285. package/dist/src/versions.d.ts +9 -0
  286. package/dist/src/versions.js +11 -0
  287. package/dist/src/versions.js.map +1 -0
  288. package/dist/src/worker/index.d.ts +3 -0
  289. package/dist/src/worker/index.js +57 -0
  290. package/dist/src/worker/index.js.map +1 -0
  291. package/dist/src/worker/main.d.ts +2 -0
  292. package/dist/src/worker/main.js +9 -0
  293. package/dist/src/worker/main.js.map +1 -0
  294. package/dist/src/worker/worker.d.ts +45 -0
  295. package/dist/src/worker/worker.js +154 -0
  296. package/dist/src/worker/worker.js.map +1 -0
  297. package/kernel/runner.py +507 -0
  298. package/package.json +83 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=service-contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-contracts.js","sourceRoot":"","sources":["../../../../src/integrations/lingxiloop/service-contracts.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import type { SqlQueryable } from '../../control-plane/pg-store.js';
2
+ import type { WorkItem } from '../../protocol/types.js';
3
+ import type { LingxiLoopServices } from './service-contracts.js';
4
+ export declare class TeacherScopeError extends Error {
5
+ }
6
+ export declare function teacherContext(work: Pick<WorkItem, 'tenantId' | 'agentId' | 'sessionId' | 'principalId' | 'kind'>, services: Pick<LingxiLoopServices, 'teacher' | 'permissionService'>, database: SqlQueryable): Promise<{
7
+ agent: {
8
+ id: string;
9
+ name: string;
10
+ projectId: string;
11
+ };
12
+ course: {
13
+ id: string;
14
+ projectId: string;
15
+ title: string;
16
+ status: string;
17
+ };
18
+ room: {
19
+ id: string;
20
+ status: string;
21
+ };
22
+ trigger: {
23
+ mode: string;
24
+ teacherId: string;
25
+ };
26
+ counts: {
27
+ learners: number;
28
+ objectives: number;
29
+ activities: number;
30
+ pendingReviews: number;
31
+ };
32
+ }>;
@@ -0,0 +1,25 @@
1
+ export class TeacherScopeError extends Error {
2
+ }
3
+ export async function teacherContext(work, services, database) {
4
+ if (!work.principalId)
5
+ throw new Error('teacher principal is required');
6
+ if (!services.teacher)
7
+ throw new Error('native teacher services are required');
8
+ await services.permissionService.assertCan({ actorUserId: work.principalId, companyId: work.tenantId,
9
+ action: 'conversation:read', resource: { type: 'conversation', id: work.sessionId } });
10
+ const scope = await services.teacher.findTeacherScopeBinding(database, work.tenantId, work.agentId, work.sessionId);
11
+ if (!scope || scope.company_id !== work.tenantId || scope.agent_id !== work.agentId || scope.room_id !== work.sessionId
12
+ || scope.room_status !== 'active' || scope.course_status === 'ARCHIVED' || !scope.has_teacher
13
+ || !scope.course_id || !scope.project_id)
14
+ throw new TeacherScopeError('teacher scope does not match the persisted request principal');
15
+ await services.teacher.requireLearningCourseRole(database, { companyId: work.tenantId, courseId: scope.course_id, userId: work.principalId, role: 'teacher' });
16
+ const counts = await services.teacher.findTeacherTurnCounts(database, work.tenantId, scope.project_id);
17
+ return {
18
+ agent: { id: scope.agent_id, name: scope.agent_name, projectId: scope.project_id },
19
+ course: { id: scope.course_id, projectId: scope.project_id, title: scope.course_title, status: scope.course_status },
20
+ room: { id: scope.room_id, status: scope.room_status },
21
+ trigger: { mode: work.kind === 'teacher_digest' ? 'routine' : work.kind === 'resume' ? 'approval' : 'teacher', teacherId: work.principalId },
22
+ counts: { learners: Number(counts.learners), objectives: Number(counts.objectives), activities: Number(counts.activities), pendingReviews: Number(counts.pending_reviews) },
23
+ };
24
+ }
25
+ //# sourceMappingURL=teacher-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teacher-context.js","sourceRoot":"","sources":["../../../../src/integrations/lingxiloop/teacher-context.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,iBAAkB,SAAQ,KAAK;CAAG;AAE/C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAmF,EAAE,QAAmE,EAAE,QAAsB;IACnN,IAAI,CAAC,IAAI,CAAC,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA;IACvE,IAAI,CAAC,QAAQ,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAA;IAC9E,MAAM,QAAQ,CAAC,iBAAiB,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ;QAClG,MAAM,EAAE,mBAAmB,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IACxF,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,uBAAuB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IACnH,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC,SAAS;WAClH,KAAK,CAAC,WAAW,KAAK,QAAQ,IAAI,KAAK,CAAC,aAAa,KAAK,UAAU,IAAI,CAAC,KAAK,CAAC,WAAW;WAC1F,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,UAAU;QAAE,MAAM,IAAI,iBAAiB,CAAC,8DAA8D,CAAC,CAAA;IACvI,MAAM,QAAQ,CAAC,OAAO,CAAC,yBAAyB,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAA;IAC9J,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,qBAAqB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;IACtG,OAAO;QACL,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,SAAS,EAAE,KAAK,CAAC,UAAU,EAAE;QAClF,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,aAAa,EAAE;QACpH,IAAI,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,WAAW,EAAE;QACtD,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,WAAW,EAAE;QAC5I,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE;KAC5K,CAAA;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { type SqlPool, type SqlQueryable } from '../../control-plane/pg-store.js';
2
+ import type { WorkItem } from '../../protocol/types.js';
3
+ import type { LingxiLoopServices } from './service-contracts.js';
4
+ import { teacherContext } from './teacher-context.js';
5
+ type Services = Pick<LingxiLoopServices, 'teacher' | 'permissionService'>;
6
+ type Context = Awaited<ReturnType<typeof teacherContext>>;
7
+ export declare const TEACHER_DIGEST_METHODS: readonly ["current", "overview", "get_digest_schedule"];
8
+ export declare function cancelRoutineRuns(database: SqlQueryable, id: string): Promise<void>;
9
+ export declare function getTeacherDigest(database: SqlQueryable, work: Pick<WorkItem, 'tenantId' | 'agentId' | 'sessionId'>, context: Context): Promise<Record<string, unknown>>;
10
+ export declare function configureTeacherDigest(database: SqlPool, services: Services, work: Omit<WorkItem, 'leaseToken'>, args: Record<string, unknown>): Promise<Record<string, unknown>>;
11
+ export declare function assertTeacherDigestWork(database: SqlQueryable, work: Omit<WorkItem, 'leaseToken'>, context: Context): Promise<void>;
12
+ export declare function scheduleTeacherDigests(database: SqlPool, services: Services): Promise<number>;
13
+ export {};
@@ -0,0 +1,152 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { withTransaction } from '../../control-plane/pg-store.js';
3
+ import { teacherContext, TeacherScopeError } from './teacher-context.js';
4
+ const weekdays = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
5
+ export const TEACHER_DIGEST_METHODS = ['current', 'overview', 'get_digest_schedule'];
6
+ const instruction = 'Generate the scheduled teacher digest using host.teacher.overview(windowDays=7). Summarize bounded aggregate course facts, current evidence coverage, and observed attention items. Do not infer hidden traits, read raw attempts, perform writes, contact learners, or ask questions. Clearly state unavailable information.';
7
+ function instant(value) { return value instanceof Date ? value : new Date(String(value)); }
8
+ function schedule(args) {
9
+ const frequency = args['frequency'];
10
+ if (frequency !== 'daily' && frequency !== 'weekly')
11
+ throw new Error('frequency must be daily, weekly, or off');
12
+ const localTime = args['localTime'], timezone = args['timezone'] ?? 'Asia/Shanghai';
13
+ if (typeof timezone !== 'string' || timezone.length > 100)
14
+ throw new Error('invalid timezone');
15
+ try {
16
+ if (/^[+-]/.test(new Intl.DateTimeFormat('en-US', { timeZone: timezone }).resolvedOptions().timeZone))
17
+ throw new Error('offset');
18
+ }
19
+ catch {
20
+ throw new Error('timezone must be a valid IANA timezone');
21
+ }
22
+ if (typeof localTime !== 'string' || !/^([01]\d|2[0-3]):[0-5]\d$/.test(localTime))
23
+ throw new Error('localTime must use HH:mm');
24
+ const weekday = args['weekday'];
25
+ if (frequency === 'weekly' ? typeof weekday !== 'string' || !weekdays.includes(weekday) : weekday !== undefined)
26
+ throw new Error('weekday is required only for weekly digests');
27
+ return { frequency: frequency, localTime, timezone, ...(typeof weekday === 'string' ? { weekday } : {}) };
28
+ }
29
+ async function nextRun(database, value, now) {
30
+ // PostgreSQL resolves missing/repeated local times using its timezone rules.
31
+ const { rows } = await database.query(`WITH candidates AS (
32
+ SELECT (($1::timestamptz AT TIME ZONE $2)::date + day + $3::time) AS local_time
33
+ FROM generate_series(0,7) AS day
34
+ ) SELECT MIN(local_time AT TIME ZONE $2) AS next_run_at FROM candidates
35
+ WHERE (local_time AT TIME ZONE $2)>$1::timestamptz
36
+ AND ($4='daily' OR EXTRACT(ISODOW FROM local_time)=$5::int)`, [now, value.timezone, value.localTime, value.frequency, value.weekday ? weekdays.indexOf(value.weekday) + 1 : 1]);
37
+ const parsed = instant(rows[0]?.['next_run_at']);
38
+ if (!Number.isFinite(parsed.getTime()) || parsed <= now)
39
+ throw new Error('digest schedule did not return a future instant');
40
+ return parsed.toISOString();
41
+ }
42
+ export async function cancelRoutineRuns(database, id) {
43
+ await database.query(`UPDATE lingxios.agent_work_items work SET cancel_requested_at=COALESCE(cancel_requested_at,NOW()),
44
+ status=CASE WHEN status='queued' THEN 'cancelled' ELSE status END,updated_at=NOW()
45
+ FROM lingxios.agent_routine_runs run WHERE run.routine_id=$1 AND run.work_id=work.id
46
+ AND (work.status IN ('queued','leased') OR EXISTS(SELECT 1 FROM lingxios.agent_delivery_outbox outbox WHERE outbox.run_id=work.id AND outbox.delivered_at IS NULL))`, [id]);
47
+ }
48
+ export async function getTeacherDigest(database, work, context) {
49
+ const { rows } = await database.query(`SELECT schedule,timezone,status,version,next_run_at,pause_reason FROM lingxios.agent_routines
50
+ WHERE tenant_id=$1 AND agent_id=$2 AND session_id=$3 AND course_id=$4 AND project_id=$5 AND kind='teacher_digest'`, [work.tenantId, work.agentId, work.sessionId, context.course.id, context.agent.projectId]);
51
+ const row = rows[0];
52
+ if (!row)
53
+ return { frequency: 'off', timezone: 'Asia/Shanghai', status: 'paused' };
54
+ return { ...row['schedule'], timezone: row['timezone'], status: row['status'], version: row['version'],
55
+ ...(row['status'] === 'active' ? { nextRunAt: instant(row['next_run_at']).toISOString() } : { frequency: 'off' }),
56
+ ...(row['pause_reason'] ? { pauseReason: row['pause_reason'] } : {}) };
57
+ }
58
+ export async function configureTeacherDigest(database, services, work, args) {
59
+ if (work.kind === 'teacher_digest')
60
+ throw new Error('scheduled teacher summaries are read-only');
61
+ if (Object.keys(args).some(key => !['frequency', 'localTime', 'timezone', 'weekday'].includes(key)))
62
+ throw new Error('unknown digest argument');
63
+ const value = args['frequency'] === 'off' ? undefined : schedule(args);
64
+ if (!value && Object.keys(args).some(key => key !== 'frequency'))
65
+ throw new Error('pausing a digest only accepts frequency=off');
66
+ return withTransaction(database, async (client) => {
67
+ const context = await teacherContext(work, services, client);
68
+ const id = 'teacher-digest-' + createHash('sha256').update(JSON.stringify([work.tenantId, work.agentId, work.sessionId])).digest('hex');
69
+ if (!value) {
70
+ await client.query(`UPDATE lingxios.agent_routines SET status='paused',next_run_at=NULL,version=version+1,pause_reason='requested',updated_at=NOW()
71
+ WHERE id=$1 AND tenant_id=$2`, [id, work.tenantId]);
72
+ }
73
+ else {
74
+ const now = (await client.query('SELECT NOW() AS now')).rows[0]['now'];
75
+ const next = await nextRun(client, value, instant(now));
76
+ await client.query(`INSERT INTO lingxios.agent_routines(id,tenant_id,agent_id,session_id,principal_id,project_id,course_id,kind,schedule,timezone,status,next_run_at)
77
+ VALUES($1,$2,$3,$4,$5,$6,$7,'teacher_digest',$8::jsonb,$9,'active',$10)
78
+ ON CONFLICT(tenant_id,agent_id,session_id,kind) WHERE kind='teacher_digest' DO UPDATE SET principal_id=EXCLUDED.principal_id,project_id=EXCLUDED.project_id,course_id=EXCLUDED.course_id,
79
+ schedule=EXCLUDED.schedule,timezone=EXCLUDED.timezone,status='active',next_run_at=EXCLUDED.next_run_at,
80
+ version=lingxios.agent_routines.version+1,pause_reason=NULL,updated_at=NOW()`, [id, work.tenantId, work.agentId, work.sessionId, work.principalId, context.agent.projectId, context.course.id, JSON.stringify(value), value.timezone, next]);
81
+ }
82
+ await cancelRoutineRuns(client, id);
83
+ services.teacher.inc('learning.teacher_agent.digest_configured', { frequency: value?.frequency ?? 'off' });
84
+ return getTeacherDigest(client, work, context);
85
+ });
86
+ }
87
+ export async function assertTeacherDigestWork(database, work, context) {
88
+ const { rows } = await database.query(`SELECT 1 FROM lingxios.agent_routines routine
89
+ JOIN lingxios.agent_routine_runs run ON run.routine_id=routine.id AND run.routine_version=routine.version
90
+ JOIN lingxios.agent_work_items work ON work.id=run.work_id
91
+ WHERE run.work_id=$1 AND routine.status='active' AND routine.kind='teacher_digest'
92
+ AND routine.tenant_id=$2 AND routine.agent_id=$3 AND routine.session_id=$4 AND routine.principal_id=$5
93
+ AND routine.course_id=$6 AND routine.project_id=$7 AND work.kind='teacher_digest'
94
+ AND work.tenant_id=routine.tenant_id AND work.agent_id=routine.agent_id AND work.session_id=routine.session_id
95
+ AND work.principal_id=routine.principal_id AND work.thread_id IS NULL AND work.cancel_requested_at IS NULL`, [work.id, work.tenantId, work.agentId, work.sessionId, work.principalId, context.course.id, context.agent.projectId]);
96
+ if (rows.length !== 1 || work.threadId !== undefined)
97
+ throw new Error('scheduled teacher work is cancelled, obsolete, or outside its authorized routine');
98
+ }
99
+ export async function scheduleTeacherDigests(database, services) {
100
+ if (!services.teacher)
101
+ return 0;
102
+ return withTransaction(database, async (client) => {
103
+ await client.query("SET LOCAL lock_timeout='2s'");
104
+ await client.query("SET LOCAL statement_timeout='5s'");
105
+ const { rows } = await client.query(`SELECT routine.*,NOW() AS clock,
106
+ EXISTS(SELECT 1 FROM lingxios.agent_routine_runs run JOIN lingxios.agent_work_items work ON work.id=run.work_id
107
+ WHERE run.routine_id=routine.id AND work.cancel_requested_at IS NULL
108
+ AND (work.status IN ('queued','leased') OR EXISTS(SELECT 1 FROM lingxios.agent_delivery_outbox outbox WHERE outbox.run_id=work.id AND outbox.delivered_at IS NULL))) AS pending
109
+ FROM lingxios.agent_routines routine WHERE routine.status='active' AND routine.kind='teacher_digest' AND routine.next_run_at<=NOW()
110
+ ORDER BY routine.next_run_at,routine.id LIMIT 8 FOR UPDATE OF routine SKIP LOCKED`);
111
+ let enqueued = 0;
112
+ for (const row of rows) {
113
+ const identity = { tenantId: String(row['tenant_id']), agentId: String(row['agent_id']), sessionId: String(row['session_id']), principalId: String(row['principal_id']), kind: 'teacher_digest' };
114
+ try {
115
+ const context = await teacherContext(identity, services, client);
116
+ if (context.course.id !== row['course_id'] || context.agent.projectId !== row['project_id'])
117
+ throw new TeacherScopeError('scheduled course binding changed');
118
+ }
119
+ catch (error) {
120
+ if (!(error instanceof TeacherScopeError) && !(error instanceof Error && error.name === 'ForbiddenError' && [403, 404].includes(Reflect.get(error, 'status'))))
121
+ throw error;
122
+ await client.query("UPDATE lingxios.agent_routines SET status='paused',next_run_at=NULL,version=version+1,pause_reason=$2,updated_at=NOW() WHERE id=$1", [row['id'], error instanceof TeacherScopeError ? 'scope_changed' : 'authorization_revoked']);
123
+ await cancelRoutineRuns(client, String(row['id']));
124
+ continue;
125
+ }
126
+ const scheduledAt = instant(row['next_run_at']).toISOString();
127
+ const id = 'digest-run-' + createHash('sha256').update(JSON.stringify([row['id'], row['version'], scheduledAt])).digest('hex');
128
+ let value;
129
+ try {
130
+ value = schedule({ ...row['schedule'], timezone: row['timezone'] });
131
+ }
132
+ catch {
133
+ await client.query("UPDATE lingxios.agent_routines SET status='paused',next_run_at=NULL,version=version+1,pause_reason='invalid_schedule',updated_at=NOW() WHERE id=$1", [row['id']]);
134
+ await cancelRoutineRuns(client, String(row['id']));
135
+ continue;
136
+ }
137
+ const next = await nextRun(client, value, instant(row['clock']));
138
+ const existing = await client.query('SELECT work_id FROM lingxios.agent_routine_runs WHERE routine_id=$1 AND routine_version=$2 AND scheduled_at=$3', [row['id'], row['version'], scheduledAt]);
139
+ if (row['pending'] || existing.rows.length) {
140
+ await client.query('UPDATE lingxios.agent_routines SET next_run_at=$2,updated_at=NOW() WHERE id=$1', [row['id'], next]);
141
+ continue;
142
+ }
143
+ await client.query(`INSERT INTO lingxios.agent_work_items(id,tenant_id,agent_id,session_id,principal_id,kind,lane,trigger_ref,priority,meta)
144
+ VALUES($1,$2,$3,$4,$5,'teacher_digest','background',$1,-10,$6::jsonb)`, [id, identity.tenantId, identity.agentId, identity.sessionId, identity.principalId, JSON.stringify({ text: instruction, authorName: 'Scheduled teacher digest', routineId: row['id'], routineVersion: row['version'], scheduledAt })]);
145
+ await client.query('INSERT INTO lingxios.agent_routine_runs(routine_id,routine_version,scheduled_at,work_id) VALUES($1,$2,$3,$4)', [row['id'], row['version'], scheduledAt, id]);
146
+ await client.query('UPDATE lingxios.agent_routines SET next_run_at=$2,updated_at=NOW() WHERE id=$1', [row['id'], next]);
147
+ enqueued++;
148
+ }
149
+ return enqueued;
150
+ });
151
+ }
152
+ //# sourceMappingURL=teacher-digest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teacher-digest.js","sourceRoot":"","sources":["../../../../src/integrations/lingxiloop/teacher-digest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { SqlQueryable } from '../../control-plane/pg-store.js';
2
+ import type { HostAction } from '../../protocol/types.js';
3
+ import type { LingxiLoopServices } from './service-contracts.js';
4
+ import type { teacherContext } from './teacher-context.js';
5
+ export declare function teacherPreview(database: SqlQueryable, teacher: NonNullable<LingxiLoopServices['teacher']>, companyId: string, context: Awaited<ReturnType<typeof teacherContext>>, action: HostAction): Promise<{
6
+ requestedBy: string;
7
+ summary: string;
8
+ scope: {
9
+ projectId: string;
10
+ courseId: string;
11
+ roomId: string;
12
+ risk: string;
13
+ };
14
+ preview: {
15
+ method: string;
16
+ entityId: string;
17
+ entityLabel: string | null;
18
+ currentState: string | boolean;
19
+ currentVersion: unknown;
20
+ args: Record<string, unknown>;
21
+ };
22
+ }>;
@@ -0,0 +1,41 @@
1
+ export async function teacherPreview(database, teacher, companyId, context, action) {
2
+ const method = action.action.slice('teacher.'.length), args = action.args;
3
+ const courseId = context.course.id;
4
+ let entityId, entityLabel, currentState, currentVersion;
5
+ if (method === 'set_teacher_membership') {
6
+ entityId = String(args['userId']).trim();
7
+ const target = await teacher.findTeacherMembershipApprovalTarget(database, companyId, courseId, entityId);
8
+ entityLabel = target.label;
9
+ currentState = currentVersion = target.enabled;
10
+ }
11
+ else if (method === 'review_evaluation') {
12
+ entityId = String(args['evaluationId']).trim();
13
+ const target = await teacher.findTeacherEvaluationApprovalTarget(database, companyId, courseId, entityId);
14
+ if (!target)
15
+ throw new Error('evaluation is outside the current course');
16
+ entityLabel = target.label;
17
+ currentState = currentVersion = target.status;
18
+ }
19
+ else {
20
+ entityId = method === 'transition_course' ? courseId : String(args[method.includes('objective') ? 'objectiveId' : 'activityId']).trim();
21
+ const target = method === 'transition_course' ? await teacher.findTeacherCourseApprovalTarget(database, companyId, courseId)
22
+ : method.includes('objective') ? await teacher.findTeacherObjectiveApprovalTarget(database, companyId, courseId, entityId)
23
+ : await teacher.findTeacherActivityApprovalTarget(database, companyId, courseId, entityId);
24
+ if (!target)
25
+ throw new Error('teacher approval target is outside the current course');
26
+ entityLabel = target.label;
27
+ currentState = target.status;
28
+ const version = target.updatedAt;
29
+ currentVersion = version instanceof Date ? version.toISOString()
30
+ : typeof version === 'string' && /[T ]/.test(version) && Number.isFinite(Date.parse(version)) ? new Date(version).toISOString() : version;
31
+ }
32
+ const labels = { publish_objective: '发布学习目标', archive_objective: '归档学习目标', publish_activity: '发布学习活动', close_activity: '关闭学习活动',
33
+ transition_course: { END: '结束课程', ENTER_READ_ONLY: '进入只读', ARCHIVE: '归档课程' }[String(args['command'])] ?? '推进课程生命周期',
34
+ set_teacher_membership: args['enabled'] === false ? '移除教师身份' : '授予教师身份', review_evaluation: args['decision'] === 'reject' ? '退回学习评价' : '采纳学习评价' };
35
+ if (!Object.hasOwn(labels, method))
36
+ throw new Error('unsupported teacher approval');
37
+ return { requestedBy: context.trigger.teacherId, summary: `${labels[method]}“${entityLabel ?? '当前对象'}”`,
38
+ scope: { projectId: context.agent.projectId, courseId, roomId: context.room.id, risk: method === 'review_evaluation' ? 'learning_evaluation' : 'course_management' },
39
+ preview: { method, entityId, entityLabel, currentState, currentVersion, args } };
40
+ }
41
+ //# sourceMappingURL=teacher-preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teacher-preview.js","sourceRoot":"","sources":["../../../../src/integrations/lingxiloop/teacher-preview.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { type SqlQueryable, type SqlPool } from '../../control-plane/pg-store.js';
2
+ import type { HostAction, WorkItem } from '../../protocol/types.js';
3
+ import type { LingxiLoopServices, NativeQueryable } from './service-contracts.js';
4
+ export declare const TEACHER_METHODS: {
5
+ readonly set_learner_membership: readonly ["userId", "enabled"];
6
+ readonly set_room_binding: readonly ["conversationId", "enabled", "purpose"];
7
+ readonly draft_activity: readonly ["title", "instructions", "type", "evaluationMode", "targetLevel", "rubric", "objectiveIds", "dueAt"];
8
+ readonly draft_objectives: readonly ["objectives"];
9
+ readonly update_course: readonly ["title", "description"];
10
+ readonly current: readonly [];
11
+ readonly overview: readonly ["windowDays"];
12
+ readonly list_learners: readonly ["attentionOnly"];
13
+ readonly get_learner: readonly ["learnerId"];
14
+ readonly get_attempt: readonly ["attemptId"];
15
+ readonly list_objectives: readonly [];
16
+ readonly list_activities: readonly [];
17
+ readonly list_reviews: readonly [];
18
+ readonly list_rooms: readonly [];
19
+ readonly get_digest_schedule: readonly [];
20
+ readonly configure_digest: readonly ["frequency", "localTime", "timezone", "weekday"];
21
+ };
22
+ export declare function executeTeacher(work: Omit<WorkItem, 'leaseToken'>, action: HostAction, services: Pick<LingxiLoopServices, 'teacher' | 'permissionService'>, database?: SqlPool): Promise<object>;
23
+ /** Keep native domain transactions on the already-open package transaction. */
24
+ export declare function teacherTransaction(client: SqlQueryable): <T>(run: (db: NativeQueryable) => Promise<T>) => Promise<T>;
@@ -0,0 +1,224 @@
1
+ import { teacherContext } from './teacher-context.js';
2
+ import { withTransaction } from '../../control-plane/pg-store.js';
3
+ import { assertTeacherDigestWork, configureTeacherDigest, getTeacherDigest, TEACHER_DIGEST_METHODS } from './teacher-digest.js';
4
+ export const TEACHER_METHODS = { set_learner_membership: ['userId', 'enabled'], set_room_binding: ['conversationId', 'enabled', 'purpose'], draft_activity: ['title', 'instructions', 'type', 'evaluationMode', 'targetLevel', 'rubric', 'objectiveIds', 'dueAt'], draft_objectives: ['objectives'], update_course: ['title', 'description'], current: [], overview: ['windowDays'], list_learners: ['attentionOnly'], get_learner: ['learnerId'], get_attempt: ['attemptId'], list_objectives: [], list_activities: [], list_reviews: [], list_rooms: [], get_digest_schedule: [], configure_digest: ['frequency', 'localTime', 'timezone', 'weekday'] };
5
+ export async function executeTeacher(work, action, services, database) {
6
+ const method = action.action.slice('teacher.'.length);
7
+ if (!action.action.startsWith('teacher.') || !Object.hasOwn(TEACHER_METHODS, method))
8
+ throw new Error('unsupported teacher action');
9
+ if (work.kind === 'teacher_digest' && !TEACHER_DIGEST_METHODS.includes(method))
10
+ throw new Error('scheduled teacher summaries are read-only');
11
+ const allowed = TEACHER_METHODS[method];
12
+ if (Object.keys(action.args).some(key => !allowed.includes(key)))
13
+ throw new Error('unknown teacher argument');
14
+ if (method === 'get_learner' || method === 'get_attempt') {
15
+ const id = action.args[method === 'get_learner' ? 'learnerId' : 'attemptId'];
16
+ if (typeof id !== 'string' || !id.trim() || id.length > 2000)
17
+ throw new Error('teacher resource ID is required');
18
+ }
19
+ const days = action.args['windowDays'], attention = action.args['attentionOnly'];
20
+ if (days !== undefined && (!Number.isSafeInteger(days) || Number(days) < 1 || Number(days) > 365))
21
+ throw new Error('windowDays must be 1-365');
22
+ if (attention !== undefined && typeof attention !== 'boolean')
23
+ throw new Error('attentionOnly must be boolean');
24
+ if (method === 'set_learner_membership') {
25
+ const { userId, enabled } = action.args;
26
+ if (typeof userId !== 'string' || !userId.trim() || userId.length > 2000)
27
+ throw new Error('userId is required');
28
+ if (typeof enabled !== 'boolean')
29
+ throw new Error('enabled must be boolean');
30
+ }
31
+ if (method === 'set_room_binding') {
32
+ const { conversationId, enabled, purpose } = action.args;
33
+ if (typeof conversationId !== 'string' || !conversationId.trim() || conversationId.length > 2000)
34
+ throw new Error('conversationId is required');
35
+ if (typeof enabled !== 'boolean')
36
+ throw new Error('enabled must be boolean');
37
+ if (enabled ? purpose !== 'lab' && purpose !== 'discussion' : purpose !== undefined)
38
+ throw new Error('purpose is required only when enabling a room binding');
39
+ }
40
+ if (method === 'draft_activity') {
41
+ for (const key of ['title', 'instructions']) {
42
+ const value = action.args[key];
43
+ if (typeof value !== 'string' || !value.trim() || value.length > 10000)
44
+ throw new Error('activity text must contain 1-10000 characters');
45
+ }
46
+ const { type, evaluationMode, targetLevel, rubric, objectiveIds, dueAt } = action.args;
47
+ if (typeof type !== 'string' || !['LESSON', 'PRACTICE', 'ASSESSMENT', 'PROJECT', 'REVIEW'].includes(type))
48
+ throw new Error('invalid activity type');
49
+ if (evaluationMode !== undefined && evaluationMode !== 'AGENT_FORMATIVE' && evaluationMode !== 'TEACHER_REQUIRED')
50
+ throw new Error('invalid evaluationMode');
51
+ if (targetLevel !== undefined && (!Number.isInteger(targetLevel) || Number(targetLevel) < 1 || Number(targetLevel) > 4))
52
+ throw new Error('targetLevel must be 1-4');
53
+ if (rubric !== undefined && (!Array.isArray(rubric) || rubric.length > 100))
54
+ throw new Error('rubric must contain at most 100 items');
55
+ if (objectiveIds !== undefined && (!Array.isArray(objectiveIds) || objectiveIds.length > 100
56
+ || objectiveIds.some(id => typeof id !== 'string' || !id.trim() || id.length > 2000)
57
+ || new Set(objectiveIds).size !== objectiveIds.length))
58
+ throw new Error('invalid objectiveIds');
59
+ if (dueAt !== undefined && (typeof dueAt !== 'string' || dueAt.length > 100 || !Number.isFinite(Date.parse(dueAt))))
60
+ throw new Error('invalid dueAt');
61
+ }
62
+ if (method === 'draft_objectives') {
63
+ const objectives = action.args['objectives'];
64
+ if (!Array.isArray(objectives) || !objectives.length || objectives.length > 100)
65
+ throw new Error('objectives must contain 1-100 items');
66
+ for (const item of objectives) {
67
+ if (!item || typeof item !== 'object' || Array.isArray(item)
68
+ || Object.keys(item).some(key => !['title', 'successCriteria', 'targetLevel', 'prerequisiteIds'].includes(key)))
69
+ throw new Error('invalid objective fields');
70
+ for (const key of ['title', 'successCriteria']) {
71
+ if (typeof item[key] !== 'string' || !item[key].trim() || item[key].length > 10000)
72
+ throw new Error('objective text must contain 1-10000 characters');
73
+ }
74
+ if (item.targetLevel !== undefined && (!Number.isInteger(item.targetLevel) || item.targetLevel < 1 || item.targetLevel > 4))
75
+ throw new Error('targetLevel must be 1-4');
76
+ if (item.prerequisiteIds !== undefined && (!Array.isArray(item.prerequisiteIds) || item.prerequisiteIds.length > 100
77
+ || item.prerequisiteIds.some((id) => typeof id !== 'string' || !id.trim() || id.length > 2000)
78
+ || new Set(item.prerequisiteIds).size !== item.prerequisiteIds.length))
79
+ throw new Error('invalid prerequisiteIds');
80
+ }
81
+ }
82
+ if (method === 'update_course') {
83
+ if (!Object.keys(action.args).length)
84
+ throw new Error('title or description is required');
85
+ for (const [key, value] of Object.entries(action.args)) {
86
+ if (typeof value !== 'string' || !value.trim() || value.length > (key === 'title' ? 2000 : 10000))
87
+ throw new Error('invalid course metadata text');
88
+ }
89
+ }
90
+ if (!database)
91
+ throw new Error('teacher actions require a database');
92
+ if (method === 'configure_digest')
93
+ return configureTeacherDigest(database, services, work, action.args);
94
+ if (method === 'update_course' || method === 'draft_objectives' || method === 'draft_activity' || method === 'set_room_binding' || method === 'set_learner_membership') {
95
+ if (!database)
96
+ throw new Error('teacher updates require a database transaction');
97
+ return withTransaction(database, async (client) => {
98
+ const context = await teacherContext(work, services, client);
99
+ const teacher = services.teacher;
100
+ const transaction = teacherTransaction(client);
101
+ const scope = { companyId: work.tenantId, courseId: context.course.id };
102
+ const args = action.args;
103
+ let result;
104
+ if (method === 'update_course') {
105
+ result = await teacher.updateTeacherCourseMetadata(client, { ...scope,
106
+ ...(typeof args['title'] === 'string' ? { title: args['title'].trim() } : {}),
107
+ ...(typeof args['description'] === 'string' ? { description: args['description'].trim() } : {}) });
108
+ }
109
+ else if (method === 'draft_objectives') {
110
+ result = await teacher.createLearningObjectives(client, transaction, { ...scope, actorId: context.trigger.teacherId, actorKind: 'teacher',
111
+ objectives: args['objectives'].map(item => ({ title: String(item['title']).trim(), successCriteria: String(item['successCriteria']).trim(),
112
+ targetLevel: Number(item['targetLevel'] ?? 3), prerequisiteIds: (item['prerequisiteIds'] ?? []) })) });
113
+ }
114
+ else if (method === 'draft_activity') {
115
+ result = await teacher.createLearningActivity(client, transaction, { ...scope, actorId: context.trigger.teacherId, actorKind: 'teacher',
116
+ title: String(args['title']).trim(), instructions: String(args['instructions']).trim(), type: args['type'],
117
+ evaluationMode: (args['evaluationMode'] ?? 'TEACHER_REQUIRED'), targetLevel: Number(args['targetLevel'] ?? 2),
118
+ rubric: (args['rubric'] ?? []), objectiveIds: (args['objectiveIds'] ?? []),
119
+ ...(typeof args['dueAt'] === 'string' ? { dueAt: args['dueAt'] } : {}) });
120
+ }
121
+ else if (method === 'set_learner_membership') {
122
+ await teacher.setLearningCourseMembership(client, transaction, { ...scope, managerId: context.trigger.teacherId, userId: String(args['userId']).trim(), role: 'learner', enabled: args['enabled'] });
123
+ result = { ok: true };
124
+ }
125
+ else {
126
+ await teacher.bindLearningCourseRoom(client, { ...scope, managerId: context.trigger.teacherId, conversationId: String(args['conversationId']).trim(), enabled: args['enabled'],
127
+ ...(args['enabled'] ? { purpose: args['purpose'] } : {}) });
128
+ result = { ok: true, enabled: args['enabled'] };
129
+ }
130
+ if (method === 'set_learner_membership') {
131
+ if (!result || typeof result !== 'object' || Reflect.get(result, 'ok') !== true)
132
+ throw new Error('teacher membership change was not acknowledged');
133
+ return result;
134
+ }
135
+ if (method === 'set_room_binding') {
136
+ if (!result || typeof result !== 'object' || Reflect.get(result, 'ok') !== true
137
+ || Reflect.get(result, 'enabled') !== action.args['enabled'])
138
+ throw new Error('teacher room binding did not acknowledge the requested change');
139
+ return result;
140
+ }
141
+ if (method === 'draft_activity') {
142
+ if (!result || typeof result !== 'object' || Reflect.get(result, 'courseId') !== context.course.id
143
+ || typeof Reflect.get(result, 'id') !== 'string' || !Reflect.get(result, 'id') || Reflect.get(result, 'status') !== 'DRAFT')
144
+ throw new Error('teacher activity creation did not return a scoped draft');
145
+ return result;
146
+ }
147
+ if (method === 'draft_objectives') {
148
+ // Native creation returns the whole course inventory, including existing published objectives.
149
+ if (!Array.isArray(result) || result.length < action.args['objectives'].length
150
+ || result.some(item => !item || typeof item !== 'object' || item.courseId !== context.course.id
151
+ || typeof item.id !== 'string' || !item.id.trim())
152
+ || new Set(result.map(item => item.id)).size !== result.length)
153
+ throw new Error('teacher draft creation did not return scoped objectives');
154
+ return result;
155
+ }
156
+ if (!result || typeof result !== 'object' || !('id' in result) || result.id !== context.agent.projectId
157
+ || !('company_id' in result) || result.company_id !== work.tenantId)
158
+ throw new Error('teacher update did not return the scoped project');
159
+ for (const [key, value] of Object.entries(action.args)) {
160
+ if (Reflect.get(result, key === 'title' ? 'name' : 'description') !== value.trim())
161
+ throw new Error('teacher update did not return the requested metadata');
162
+ }
163
+ return result;
164
+ });
165
+ }
166
+ const context = await teacherContext(work, services, database);
167
+ if (work.kind === 'teacher_digest')
168
+ await assertTeacherDigestWork(database, work, context);
169
+ const teacher = services.teacher;
170
+ const scope = { companyId: work.tenantId, projectId: context.agent.projectId, courseId: context.course.id, teacherUserId: context.trigger.teacherId };
171
+ switch (method) {
172
+ case 'current': return { ...context, digest: await getTeacherDigest(database, work, context) };
173
+ case 'overview': {
174
+ const windowDays = Number(action.args['windowDays'] ?? 30);
175
+ const rows = await teacher.loadTeacherOverviewRows(database, scope, windowDays);
176
+ teacher.inc('learning.teacher_agent.summary_generated');
177
+ return { generatedAt: new Date().toISOString(), windowDays, course: { id: context.course.id, title: context.course.title },
178
+ stateDistribution: rows.distribution, missions: rows.missions, activity: rows.activity[0] ?? {}, evidenceCoverage: rows.coverage[0] ?? {},
179
+ attention: rows.attention.map(row => ({ ...row, reasons: Array.isArray(row['attention_reasons']) ? row['attention_reasons'] : [] })) };
180
+ }
181
+ case 'list_learners': return (await teacher.listTeacherLearnerRows(database, scope, action.args['attentionOnly'] === true))
182
+ .map(row => ({ ...row, attentionReasons: Array.isArray(row['attention_reasons']) ? row['attention_reasons'] : [] }));
183
+ case 'get_learner': {
184
+ const learnerId = String(action.args['learnerId']).trim();
185
+ const member = await teacher.findTeacherLearner(database, scope, learnerId);
186
+ if (!member)
187
+ throw new Error('learner is outside the current course');
188
+ const detail = await teacher.loadTeacherLearnerDetailRows(database, scope, learnerId);
189
+ teacher.inc('learning.teacher_agent.learner_drilldown');
190
+ return { learner: { id: learnerId, ...member }, ...detail };
191
+ }
192
+ case 'get_attempt': {
193
+ const attemptId = String(action.args['attemptId']).trim();
194
+ const attempt = await teacher.findTeacherAttemptDetail(database, scope, attemptId);
195
+ if (!attempt)
196
+ throw new Error('attempt is outside the current course');
197
+ await teacher.auditInTransaction(database, { kind: 'teacher_agent_attempt_access', userId: context.trigger.teacherId,
198
+ companyId: work.tenantId, detail: { courseId: context.course.id, attemptId, agentId: work.agentId } });
199
+ teacher.inc('learning.teacher_agent.evidence_accessed');
200
+ return attempt;
201
+ }
202
+ case 'list_objectives': return teacher.listTeacherObjectives(database, scope);
203
+ case 'list_activities': return teacher.listTeacherActivities(database, scope);
204
+ case 'list_reviews': return teacher.listTeacherReviews(database, scope);
205
+ case 'list_rooms': return teacher.listTeacherBindableRooms(database, scope);
206
+ case 'get_digest_schedule': return getTeacherDigest(database, work, context);
207
+ default: throw new Error('unsupported teacher action');
208
+ }
209
+ }
210
+ /** Keep native domain transactions on the already-open package transaction. */
211
+ export function teacherTransaction(client) {
212
+ const database = { query: async (text, params) => {
213
+ const result = await client.query(text, params ? [...params] : undefined);
214
+ const command = Reflect.get(result, 'command'), oid = Reflect.get(result, 'oid'), fields = Reflect.get(result, 'fields');
215
+ if (typeof command !== 'string' || typeof oid !== 'number' || !Array.isArray(fields)
216
+ || fields.some(field => !field || typeof field.name !== 'string' || typeof field.format !== 'string'
217
+ || ['tableID', 'columnID', 'dataTypeID', 'dataTypeSize', 'dataTypeModifier'].some(key => typeof field[key] !== 'number'))) {
218
+ throw new Error('native teacher domain transactions require complete pg query results');
219
+ }
220
+ return { rows: result.rows, rowCount: result.rowCount, command, oid, fields };
221
+ } };
222
+ return (run) => run(database);
223
+ }
224
+ //# sourceMappingURL=teacher.js.map