feat: integrate learning module
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
inman
2026-08-16 20:52:16 +08:00
parent 26b52d76e3
commit 01bee3188b
107 changed files with 6318 additions and 12063 deletions

168
shared/learning.ts Normal file
View File

@@ -0,0 +1,168 @@
/** Stable boundary between Makelore, downloaded course packages and Works Square. */
export const LEARNING_API_PATH = '/api/works/learning';
export const LEARNING_GENERATION_CONTRACT_VERSION = 2;
export const LEARNING_MATERIAL_MAX_FILES = 5;
export const LEARNING_MATERIAL_MAX_FILE_BYTES = 50 * 1024 * 1024;
export const LEARNING_MATERIAL_MAX_TOTAL_BYTES = 150 * 1024 * 1024;
export type LearningGenerationOptions = {
requirement: string;
enableWebSearch: boolean;
enableImageGeneration: boolean;
enableVideoGeneration: boolean;
enableTTS: boolean;
interactiveMode: boolean;
taskEngineMode: boolean;
};
export type LearningGenerationMaterialRef = {
id: string;
name: string;
mimeType: string;
size: number;
lastModified: number;
order: number;
};
/** IPC-only upload shape. File bytes are never persisted in Renderer state. */
export type LearningGenerationMaterialUpload = LearningGenerationMaterialRef & {
bytes: Uint8Array;
};
export type LearningGenerationUploadRequest = {
options: LearningGenerationOptions;
materials: LearningGenerationMaterialUpload[];
};
export type LearningRuntimeCapability =
| 'quiz-grade'
| 'pbl/v2/task/update'
| 'pbl/v2/open-task'
| 'pbl/v2/simulator'
| 'pbl/v2/instructor'
| 'pbl/v2/evaluate';
export type LearningRuntimeBridgeRequest = {
requestId: string;
courseId: string;
contentHash: string;
capability: LearningRuntimeCapability;
method: 'POST';
context: {
moduleId: string | null;
moduleContentHash: string;
anchor: {
sceneId: string;
sceneOrder?: number;
};
};
body: unknown;
};
export type LearningRuntimeBridgeEvent =
| { requestId: string; type: 'start'; status: number; contentType: string }
| { requestId: string; type: 'chunk'; chunk: Uint8Array }
| { requestId: string; type: 'end' }
| { requestId: string; type: 'error'; code: string; message: string };
export type LearningSceneKind = 'slide' | 'interactive' | 'quiz' | 'pbl';
export type LearningCourseStatus = 'generating' | 'ready' | 'published' | 'failed' | 'archived';
export type LearningSingleCourseCapabilities = {
modular?: false;
sceneKinds: LearningSceneKind[];
hasAudio: boolean;
hasWhiteboard: boolean;
hasAgent: boolean;
};
export type LearningLargeCourseCapabilities = {
modular: true;
orderedModules: true;
productionStagePerModule: true;
};
export type LearningCourseCapabilities = LearningSingleCourseCapabilities | LearningLargeCourseCapabilities;
export type LearningCourseModule = {
moduleId: string;
title: string;
summary: string | null;
sceneCount: number;
contentHash: string;
};
export type LearningCourse = {
id: string;
origin: 'user_single' | 'ops_large';
status: LearningCourseStatus;
title: string;
summary: string | null;
language: string | null;
contentHash: string;
archiveSha256: string;
archiveBytes: number;
formatVersion: number;
minPlayerVersion: string;
sceneCount: number;
/** Ordered learning units inside one product-level course. */
modules?: LearningCourseModule[];
capabilities: LearningCourseCapabilities;
createdAt: string;
publishedAt: string | null;
};
export type LearningProgress = {
courseId: string;
/** Aggregate course hash used by Works ownership and entitlement checks. */
contentHash: string;
moduleId?: string | null;
moduleContentHash?: string | null;
sceneOrder: number;
actionIndex: number | null;
positionMs: number | null;
completed: boolean;
updatedAt: string;
};
export type LearningProgressWrite = Pick<
LearningProgress,
'contentHash' | 'moduleId' | 'sceneOrder' | 'actionIndex' | 'positionMs' | 'completed'
>;
export type InstalledLearningCourse = {
schemaVersion: 1;
course: LearningCourse;
archivePath: string;
installedAt: string;
};
export type LearningClassroomPayload = {
courseId: string;
/** Aggregate package identity used to address immutable local assets. */
courseContentHash: string;
/** Aggregate hash kept as a compatibility alias for existing Stage bridges. */
contentHash: string;
moduleId: string | null;
/** Selected frozen module hash; equals courseContentHash for legacy single-classroom packages. */
moduleContentHash: string;
modules: LearningCourseModule[];
classroom: { stage: Record<string, unknown>; scenes: unknown[] };
};
export type LearningGeneration = {
contractVersion?: number;
jobId: string;
status: string;
mode?: 'single' | 'large';
step: string | null;
progress: number | null;
message: string | null;
scenesGenerated: number | null;
totalScenes: number | null;
courseId: string | null;
error: string | null;
done: boolean;
};

View File

@@ -1,12 +1,12 @@
export const BUNDLED_OPENCODE_SKILL_IDS = [
'agent-browser',
'frontend-slides',
'game-engine',
'grilling',
'planning-with-files',
] as const;
export const DEFAULT_PROJECT_AGENT_SKILL_IDS = [
'agent-browser',
'grilling',
'planning-with-files',
] as const;