Files

57 lines
1.8 KiB
TypeScript

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<RuntimeOpenRequest>;
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<RuntimeRunRequest>;
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));
}