openlearn-next 0.4.0 → 0.4.2

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 (33) hide show
  1. package/dist/assets/{BatchPickerModal-BobcoTtn.js → BatchPickerModal-BpWu0Lil.js} +1 -1
  2. package/dist/assets/ClassesView-KbHEqJto.js +22 -0
  3. package/dist/assets/{ClassroomCountdownWidget-AWnCu4JJ.js → ClassroomCountdownWidget-96WFRO9A.js} +1 -1
  4. package/dist/assets/{CloudDriveModal-Dr4kDySU.js → CloudDriveModal-DEQ1nkNk.js} +1 -1
  5. package/dist/assets/{CourseManagement-DtInD0J-.js → CourseManagement-D3ZpxY0l.js} +1 -1
  6. package/dist/assets/{CourseWizardModal-B16QBckO.js → CourseWizardModal-Ck_-M8pk.js} +1 -1
  7. package/dist/assets/{Dashboard-D1gZ_4NI.js → Dashboard-CYbpbofV.js} +1 -1
  8. package/dist/assets/{ExportWeightModal-UPsVax6L.js → ExportWeightModal-DW2bu9Zz.js} +1 -1
  9. package/dist/assets/{HelpView-BryOHwLE.js → HelpView-CtzdqhBq.js} +1 -1
  10. package/dist/assets/{ImportLessonsModal-Dnx_QR0Z.js → ImportLessonsModal-jBuIK453.js} +1 -1
  11. package/dist/assets/InteractiveCoursewareViewer-Bj7ahXMs.js +1 -0
  12. package/dist/assets/{InteractiveWhiteboard-550JMFBu.js → InteractiveWhiteboard-B7JcJvDu.js} +1 -1
  13. package/dist/assets/{LazyCourseware-CLHWQJ8g.js → LazyCourseware-CuiPxhDg.js} +2 -2
  14. package/dist/assets/{LazyWhiteboard-DJWh6sdV.js → LazyWhiteboard-BJwCEZAU.js} +2 -2
  15. package/dist/assets/LessonEditorView-DFrOp2qp.js +1 -0
  16. package/dist/assets/{LiveClassroomView-DgN2Zqe6.js → LiveClassroomView-BZ5nGee3.js} +2 -2
  17. package/dist/assets/{NotificationDetailModal-CMKHpWX1.js → NotificationDetailModal-Baqnc5pU.js} +1 -1
  18. package/dist/assets/{PluginView-BQVWPofY.js → PluginView-CCbIuZ24.js} +1 -1
  19. package/dist/assets/{QuizGeneratorModal-BeaBRn1w.js → QuizGeneratorModal-BBEDssp7.js} +1 -1
  20. package/dist/assets/StudentAssignmentView-DgIjUsXz.js +1 -0
  21. package/dist/assets/{StudentLessonView-CMPqL1ia.js → StudentLessonView-CBJbI5x6.js} +1 -1
  22. package/dist/assets/{StudentPreviewModal-0QSLnD9u.js → StudentPreviewModal-Bh8OZs0t.js} +1 -1
  23. package/dist/assets/{StudentView-9pjzjJgG.js → StudentView--uGxc5TJ.js} +2 -2
  24. package/dist/assets/{SystemResourceLibraryModal-COrhUd53.js → SystemResourceLibraryModal-BVCclcqA.js} +1 -1
  25. package/dist/assets/{TeacherView-mP2YucVr.js → TeacherView-D4Zsc0eZ.js} +2 -2
  26. package/dist/assets/{index-C70OJPV_.js → index-BKBCBKvy.js} +29 -29
  27. package/dist/index.html +1 -1
  28. package/dist/server.cjs +87 -16
  29. package/package.json +1 -1
  30. package/dist/assets/ClassesView-DtO0PJXQ.js +0 -22
  31. package/dist/assets/InteractiveCoursewareViewer-BYMKvIHi.js +0 -1
  32. package/dist/assets/LessonEditorView-DRwi0sjA.js +0 -1
  33. package/dist/assets/StudentAssignmentView-Dn4Bgqr2.js +0 -1
package/dist/index.html CHANGED
@@ -7,7 +7,7 @@
7
7
  <meta name="description" content="An AI-powered Learning Management System featuring interactive classrooms, stage management, real-time student response systems, lesson editor, auto-grading, and an extensible third-party plugin ecosystem." />
8
8
  <meta property="og:title" content="OpenLearn Next" />
9
9
  <meta property="og:description" content="An AI-powered Learning Management System featuring interactive classrooms, stage management, real-time student response systems, lesson editor, auto-grading, and an extensible third-party plugin ecosystem." />
10
- <script type="module" crossorigin src="/assets/index-C70OJPV_.js"></script>
10
+ <script type="module" crossorigin src="/assets/index-BKBCBKvy.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="/assets/vendor-utils-m0Urvtws.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-B0t7H3hh.js">
13
13
  <link rel="modulepreload" crossorigin href="/assets/vendor-icons-BRmhY6fg.js">
package/dist/server.cjs CHANGED
@@ -686,8 +686,27 @@ var init_ai_service = __esm({
686
686
  AIService = class {
687
687
  constructor(db2) {
688
688
  this.db = db2;
689
+ /**
690
+ * 注册插件 AI 上下文切片提供者(P2: ai.context.provider)。
691
+ * fn 为服务端闭包(Inline 插件直接传函数),Agent 对话时按 lessonId 调用。
692
+ * 注册表真源在 server/ai-context-registry.ts,kernel AIService 委托写入。
693
+ */
694
+ this._contextDelegates = {};
689
695
  this.gateway = new AIProviderGateway(new AIEventBus());
690
696
  }
697
+ /** 由 server 启动时接线(server/ai-context-registry.ts 的注册表为真源) */
698
+ bindContextRegistry(delegates) {
699
+ this._contextDelegates = delegates;
700
+ }
701
+ registerAIContextProvider(id, fn) {
702
+ if (!this._contextDelegates.register) {
703
+ throw new Error("[AIService] AI context registry not bound (server boot required)");
704
+ }
705
+ this._contextDelegates.register(id, fn);
706
+ }
707
+ unregisterAIContextProvider(id) {
708
+ this._contextDelegates.unregister?.(id);
709
+ }
691
710
  async generateText(prompt, options) {
692
711
  const provider = this.db.prepare(
693
712
  "SELECT id, name, api_url, api_key, model_name FROM ai_providers WHERE api_key IS NOT NULL AND api_key != '' LIMIT 1"
@@ -1499,16 +1518,16 @@ function wrapCapability(capabilityService) {
1499
1518
  })
1500
1519
  };
1501
1520
  }
1502
- function wrapPointsDimensionRegistry(registry) {
1521
+ function wrapPointsDimensionRegistry(registry2) {
1503
1522
  return {
1504
1523
  registerDimension: createSafeFunction((spec) => {
1505
- return registry.registerDimension(spec);
1524
+ return registry2.registerDimension(spec);
1506
1525
  }),
1507
1526
  getDimension: createSafeFunction((id) => {
1508
- return registry.getDimension(id);
1527
+ return registry2.getDimension(id);
1509
1528
  }),
1510
1529
  listDimensions: createSafeFunction(() => {
1511
- return registry.listDimensions();
1530
+ return registry2.listDimensions();
1512
1531
  })
1513
1532
  };
1514
1533
  }
@@ -1579,7 +1598,14 @@ function wrapAI(aiService) {
1579
1598
  throw e;
1580
1599
  }
1581
1600
  }
1582
- )
1601
+ ),
1602
+ // P2: ai.context.provider —— 插件注册 AI 上下文切片(可选方法)
1603
+ registerAIContextProvider: createSafeFunction((id, fn) => {
1604
+ return aiService.registerAIContextProvider?.(id, fn);
1605
+ }),
1606
+ unregisterAIContextProvider: createSafeFunction((id) => {
1607
+ return aiService.unregisterAIContextProvider?.(id);
1608
+ })
1583
1609
  };
1584
1610
  }
1585
1611
  async function buildContext(serviceRegistry, tracker, pluginId, manifest, db2, skipTokens, contributionRegistry) {
@@ -18779,9 +18805,9 @@ var AnalyticsCapabilityProviderHandler = class {
18779
18805
 
18780
18806
  // packages/core/capability/invocation/invocation-engine.ts
18781
18807
  var InvocationEngine = class {
18782
- constructor(registry, pipeline, eventBus) {
18808
+ constructor(registry2, pipeline, eventBus) {
18783
18809
  this.cancelledInvocations = /* @__PURE__ */ new Set();
18784
- this.registry = registry;
18810
+ this.registry = registry2;
18785
18811
  this.pipeline = pipeline;
18786
18812
  this.eventBus = eventBus;
18787
18813
  }
@@ -18821,8 +18847,8 @@ var InvocationEngine = class {
18821
18847
 
18822
18848
  // packages/core/capability/sdk/capability-sdk.ts
18823
18849
  var CapabilitySDK = class {
18824
- constructor(registry, engine, eventBus) {
18825
- this.registry = registry;
18850
+ constructor(registry2, engine, eventBus) {
18851
+ this.registry = registry2;
18826
18852
  this.engine = engine;
18827
18853
  this.eventBus = eventBus;
18828
18854
  }
@@ -20855,6 +20881,43 @@ function attachClassroomFeedService(feedService, eventBus) {
20855
20881
  };
20856
20882
  }
20857
20883
 
20884
+ // server/ai-context-registry.ts
20885
+ var registry = {
20886
+ providers: /* @__PURE__ */ new Map()
20887
+ };
20888
+ function registerAIContextProvider(id, fn, registeredBy) {
20889
+ if (!id?.trim() || typeof fn !== "function") {
20890
+ throw new Error("[AIContextRegistry] id and provider function are required");
20891
+ }
20892
+ registry.providers.set(id.trim(), { fn, registeredBy });
20893
+ }
20894
+ function unregisterAIContextProvider(id, registeredBy) {
20895
+ const entry = registry.providers.get(id.trim());
20896
+ if (entry && registeredBy && entry.registeredBy && entry.registeredBy !== registeredBy) return;
20897
+ registry.providers.delete(id.trim());
20898
+ }
20899
+ function collectAIContextSlices(lessonId) {
20900
+ const slices = [];
20901
+ for (const [id, entry] of registry.providers) {
20902
+ try {
20903
+ const slice = entry.fn(lessonId);
20904
+ if (typeof slice === "string" && slice.trim()) {
20905
+ slices.push(`[Plugin Context \xB7 ${id}]
20906
+ ${slice.trim()}`);
20907
+ }
20908
+ } catch (err) {
20909
+ console.error(`[AIContextRegistry] provider "${id}" failed:`, err);
20910
+ }
20911
+ }
20912
+ return slices;
20913
+ }
20914
+ function bindAIContextRegistry(kernelAIService) {
20915
+ kernelAIService.bindContextRegistry({
20916
+ register: (id, fn) => registerAIContextProvider(id, fn),
20917
+ unregister: (id) => unregisterAIContextProvider(id)
20918
+ });
20919
+ }
20920
+
20858
20921
  // server.ts
20859
20922
  var import_helmet = __toESM(require("helmet"), 1);
20860
20923
  var import_express_rate_limit = __toESM(require("express-rate-limit"), 1);
@@ -21588,7 +21651,7 @@ function simpleChecksum(text) {
21588
21651
 
21589
21652
  // server/ai-agent.ts
21590
21653
  var import_crypto4 = __toESM(require("crypto"), 1);
21591
- var buildAgentSystemInstruction = (lang, currentLessonId) => {
21654
+ var buildAgentSystemInstruction = (lang, currentLessonId, pluginSlices) => {
21592
21655
  let systemInstruction = lang === "zh" ? "\u4F60\u662F\u4E00\u4E2A\u6559\u80B2\u7CFB\u7EDF\u5E95\u5C42\u7684 OS Agent\u3002\u4F60\u9700\u8981\u7406\u89E3\u8001\u5E08\u7684\u6307\u4EE4\uFF0C\u5E76\u8C03\u7528\u53EF\u7528\u7684\u5DE5\u5177\uFF08\u547D\u4EE4\uFF09\u53BB\u6267\u884C\u8FD9\u4E9B\u64CD\u4F5C\u3002\u5982\u679C\u8001\u5E08\u8BA9\u4F60\u521B\u5EFA\u4E00\u8282\u8BFE\uFF0C\u8BF7\u52A1\u5FC5\u5229\u7528\u5DE5\u5177\u751F\u6210\u8BE6\u7EC6\u7684\u521D\u59CB\u8BFE\u7A0B\u5185\u5BB9\u3002\u5982\u679C\u8001\u5E08\u8981\u6C42\u7BA1\u7406\u8FDB\u7A0B/\u4EFB\u52A1\uFF0C\u8BF7\u4F7F\u7528 process.spawn, process.kill, process.list\u3002\u5982\u679C\u9700\u5B58\u50A8\u6587\u4EF6\u3001\u7D20\u6750\u6216\u521B\u5EFA\u76EE\u5F55\uFF0C\u8BF7\u4F7F\u7528 vfs.* \u5E76\u5728\u9700\u8981\u65F6\u7BA1\u7406\u73ED\u7EA7\u548C\u5B66\u751F\u3002\u4F60\u652F\u6301\u901A\u8FC7 class_create \u521B\u5EFA\u73ED\u7EA7, student_create \u521B\u5EFA\u5B66\u751F, class_add_student \u5C06\u5B66\u751F\u52A0\u5165\u73ED\u7EA7\u3002\u5F53\u8001\u5E08\u8981\u6C42\u4ECE\u63D0\u4F9B\u7684\u6570\u636E\uFF08\u5982CSV\u3001JSON\u3001Markdown\u6216\u5BF9\u8BDD\u4E2D\uFF09\u521B\u5EFA\u73ED\u7EA7\u6216\u5B66\u751F\u65F6\uFF0C\u8BF7\u4F9D\u6B21\u53D1\u51FA\u8FD9\u4E9B\u6307\u4EE4\u3002\u5982\u679C\u4E0A\u4E00\u9636\u6BB5\u8FD4\u56DE\u4E86\u521B\u5EFA\u6210\u529F\u7684\u73ED\u7EA7ID\u6216\u5B66\u751FID\uFF0C\u4F60\u9700\u8981\u5728\u540E\u7EED\uFFFD? functionCall \u4E2D\u5F15\u7528\u8FD9\u4E9BID\uFF08\u4F8B\u5982\uFF1A\u628A\u521A\u521B\u5EFA\u7684\u5B66\u751FID\u52A0\u5165\u5230\u521A\u521B\u5EFA\u7684\u73ED\u7EA7ID\u4E2D\uFF09\u3002\u901A\u8FC7\u5F80\u590D\u7684\u5DE5\u5177\u8C03\u7528\uFF0C\u4F60\u53EF\u4EE5\u81EA\u52A8\u5B8C\u6210\u5B8C\u6574\u7684\u6D41\u7A0B\uFFFD?" : "You are an educational OS kernel agent. You interpret teacher instructions and use your available tools (commands) to execute them. If the teacher asks to create a lesson, always generate some detailed initial content for it. If the teacher asks to spawn or kill processes, use process tools. Use vfs tools to store assets, and manage classes/students as necessary. You support class_create, student_create, class_add_student. Always use tool chaining if you need to create a class and enroll students: first call class_create/student_create, receive their returned IDs, and then call class_add_student in the next turn. Always answer with a helpful summary.";
21593
21656
  if (currentLessonId) {
21594
21657
  systemInstruction += `
@@ -21596,6 +21659,9 @@ var buildAgentSystemInstruction = (lang, currentLessonId) => {
21596
21659
 
21597
21660
  Available tools (functions) can be used multiple times in sequence if needed.`;
21598
21661
  }
21662
+ if (pluginSlices?.length) {
21663
+ systemInstruction += "\n\n" + pluginSlices.join("\n\n");
21664
+ }
21599
21665
  return systemInstruction;
21600
21666
  };
21601
21667
  var buildAgentFinalMessage = (message, attachments) => {
@@ -21728,7 +21794,11 @@ var runGeminiAgentChat = async (request) => {
21728
21794
  };
21729
21795
  var runOpenAIAgentChat = async (provider, request) => {
21730
21796
  const { message, lang = "zh", currentLessonId, attachments, callerRole, history } = request;
21731
- const systemInstruction = buildAgentSystemInstruction(lang, currentLessonId);
21797
+ const systemInstruction = buildAgentSystemInstruction(
21798
+ lang,
21799
+ currentLessonId,
21800
+ collectAIContextSlices(currentLessonId ?? null)
21801
+ );
21732
21802
  const finalMessage = buildAgentFinalMessage(message, attachments);
21733
21803
  const tools = buildOpenAITools();
21734
21804
  const chatUrl = buildOpenAIChatUrl(provider.api_url);
@@ -22389,13 +22459,13 @@ var OFFICIAL_ACTIVITY_DEFINITIONS = [
22389
22459
  commandType: "assignment.create"
22390
22460
  }
22391
22461
  ];
22392
- function registerOfficialActivities(registry, actionRegistry) {
22462
+ function registerOfficialActivities(registry2, actionRegistry) {
22393
22463
  for (const def of OFFICIAL_ACTIVITY_DEFINITIONS) {
22394
22464
  const descriptor = {
22395
22465
  ...def,
22396
22466
  aiAction: aiActionFor(def)
22397
22467
  };
22398
- registry.registerProvider(new BaseActivityProvider({ descriptor }));
22468
+ registry2.registerProvider(new BaseActivityProvider({ descriptor }));
22399
22469
  if (actionRegistry && descriptor.aiAction) {
22400
22470
  actionRegistry.register(descriptor.aiAction);
22401
22471
  }
@@ -23237,9 +23307,9 @@ var BLOCKED_EXTENSIONS = [".exe", ".sh", ".bat", ".cmd", ".dll", ".so", ".dylib"
23237
23307
  function collectCoursewareRuntimeScripts(cwInfo) {
23238
23308
  const empty = { head: "", bodyEnd: "" };
23239
23309
  try {
23240
- const registry = kernelContainer?.serviceRegistry?.resolve?.(ICoursewareRuntimeScriptRegistryToken);
23241
- if (!registry || typeof registry.list !== "function") return empty;
23242
- const scripts = registry.list({ id: cwInfo.id, uuid: cwInfo.uuid }) || [];
23310
+ const registry2 = kernelContainer?.serviceRegistry?.resolve?.(ICoursewareRuntimeScriptRegistryToken);
23311
+ if (!registry2 || typeof registry2.list !== "function") return empty;
23312
+ const scripts = registry2.list({ id: cwInfo.id, uuid: cwInfo.uuid }) || [];
23243
23313
  if (!scripts.length) return empty;
23244
23314
  let head = "";
23245
23315
  let bodyEnd = "";
@@ -33366,6 +33436,7 @@ async function startServer() {
33366
33436
  registerClassroomExtrasRoutes(ctx.app);
33367
33437
  registerClassroomPeerReviewRoutes(ctx.app);
33368
33438
  setupRealtimeBridge({ eventBus: kernelContainer.eventBus, io, db: kernelContainer.db });
33439
+ bindAIContextRegistry(kernelContainer.aiService);
33369
33440
  setupPresence({
33370
33441
  io,
33371
33442
  eventBus: kernelContainer.eventBus,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openlearn-next",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "private": false,
5
5
  "description": "A plugin-driven Educational OS / LMS platform with AI agent teaching assistant",
6
6
  "keywords": [