export type RuntimeBinding = { kind: string; key: string }; export type RuntimeOpenRequest = { session_id: string; owner_user_id: string; runtime: string; runtime_version: string; binding: RuntimeBinding; }; export type LearningAnchor = { sceneId?: string; sceneOrder?: number; sceneTitle?: string; actionIndex?: number; /** Selected module; course binding remains courseId:aggregateContentHash. */ moduleId?: string | null; moduleContentHash?: string; }; export type RuntimeRunRequest = RuntimeOpenRequest & { command_id: string; run_id: string; client_command_id: string; name: string; input: { message?: string; history?: Array<{ role: 'user' | 'assistant'; content: string }>; anchor?: LearningAnchor; }; }; export function isRuntimeOpenRequest(value: unknown): value is RuntimeOpenRequest { if (!value || typeof value !== 'object') return false; const request = value as Partial; return request.runtime === 'learning' && request.runtime_version === 'v1' && typeof request.session_id === 'string' && typeof request.owner_user_id === 'string' && request.binding?.kind === 'learning-course' && typeof request.binding.key === 'string'; } export function isRuntimeRunRequest(value: unknown): value is RuntimeRunRequest { if (!isRuntimeOpenRequest(value)) return false; const request = value as Partial; const moduleHash = request.input?.anchor?.moduleContentHash; return typeof request.command_id === 'string' && typeof request.run_id === 'string' && typeof request.client_command_id === 'string' && request.name === 'turn.submit' && Boolean(request.input) && typeof request.input?.message === 'string' && request.input.message.trim().length > 0 && (moduleHash === undefined || /^[0-9a-f]{64}$/.test(moduleHash)); }