@intellectif/lk-core 1.10.0 → 1.10.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ai-writing-DLvzBiZN.js","names":["KINDS","isRecord","listOf","instructionsOf","isRecord","isRecord","written","turnsOf","isRecord","isRecord","listOf","stringOf","listOf","stringOf","isRecord","isRecord","KINDS","isRecord","isRecord","isRecord","text","WRITES","replyShape","stringOf","listOf","written","idSource","pathForModel","problemsOf","RANK","noWorse","isRecord","isRecord","isRecord"],"sources":["../src/ai-material.ts","../src/ai-text.ts","../src/ai.ts","../src/ai-coaching.ts","../src/media-refs.ts","../src/authoring/recordings.ts","../src/variant-rules.ts","../src/authoring/paper.ts","../src/authoring/item-group.ts","../src/authoring/index.ts","../src/ai-critique.ts","../src/ai-drafts.ts","../src/ai-written.ts","../src/ai-grading.ts","../src/ai-variants.ts","../src/ai-writing.ts"],"sourcesContent":["import type { ActivityMedia } from './types/activity.js';\nimport type { AiGroupInput, AiMaterialFacts } from './types/ai.js';\nimport type { StimulusKind } from './types/item-group.js';\n\n/**\n * What a model is told about the group a question sits in: the material the\n * learner reads and sees beside it.\n *\n * Internal: the request builders in `ai.ts` and `ai-writing.ts` are its public\n * face, through their `group` input.\n */\n\n/**\n * The longest material text a model is given, in code points. A longer one is\n * cut and marked `truncated`: a long reading passage on an exam paper runs to\n * 4,000–7,000.\n */\nexport const AI_MATERIAL_MAX_LENGTH = 12_000;\n\n// Every kind and every media type, so a new one does not compile until it is\n// read here.\nconst KINDS: Record<StimulusKind, true> = {\n text: true,\n audio: true,\n video: true,\n image: true,\n mixed: true,\n};\nconst MEDIA_TYPES: Record<ActivityMedia['type'], true> = {\n image: true,\n audio: true,\n video: true,\n embed: true,\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/** A string with something in it, as written; otherwise nothing. */\nconst filled = (value: unknown): string | undefined =>\n typeof value === 'string' && value.trim() !== '' ? value : undefined;\n\n/**\n * `text` cut to {@link AI_MATERIAL_MAX_LENGTH} code points. The cut falls at\n * the last space in its second half, so no word is broken; in a script written\n * without spaces, at the limit itself.\n */\nfunction cut(text: string): { text: string; truncated: boolean } {\n const points = [...text];\n if (points.length <= AI_MATERIAL_MAX_LENGTH) {\n return { text, truncated: false };\n }\n // From the code point just past the limit: a space there leaves every kept word whole.\n let end = AI_MATERIAL_MAX_LENGTH;\n for (let at = AI_MATERIAL_MAX_LENGTH; at >= AI_MATERIAL_MAX_LENGTH / 2; at -= 1) {\n if (/\\s/u.test(points[at] as string)) {\n end = at;\n break;\n }\n }\n return { text: points.slice(0, end).join('').trimEnd(), truncated: true };\n}\n\n/**\n * The facts of `group`'s material, or `null` without a group or a stimulus it\n * can read: not an object, or of a kind the SDK does not know.\n *\n * Only what the learner sees reaches a model. The `transcript` is author-only\n * and never read, so the facts are the same for a whole group and a redacted\n * one; a media address is never read either, since a model cannot open one\n * and a signed address must not end up in a prompt. Each field is read once.\n */\nexport function materialFacts(group: AiGroupInput | undefined): AiMaterialFacts | null {\n if (!isRecord(group)) {\n return null;\n }\n const stimulus = group.stimulus;\n if (!isRecord(stimulus)) {\n return null;\n }\n const kind = stimulus.kind;\n if (typeof kind !== 'string' || !Object.hasOwn(KINDS, kind)) {\n return null;\n }\n const title = filled(stimulus.title) ?? filled(group.title);\n const body = filled(stimulus.body);\n const text = body === undefined ? undefined : cut(body);\n const rawMedia = stimulus.media;\n const mediaType = isRecord(rawMedia) ? rawMedia.type : undefined;\n const alt = isRecord(rawMedia) ? filled(rawMedia.alt) : undefined;\n const media =\n typeof mediaType === 'string' && Object.hasOwn(MEDIA_TYPES, mediaType)\n ? { type: mediaType as ActivityMedia['type'], ...(alt !== undefined ? { alt } : {}) }\n : undefined;\n const attribution = filled(stimulus.attribution);\n const locale = filled(stimulus.locale);\n return {\n kind: kind as StimulusKind,\n ...(title !== undefined ? { title } : {}),\n ...(text !== undefined ? { text: text.text } : {}),\n ...(text?.truncated === true ? { truncated: true as const } : {}),\n ...(media !== undefined ? { media } : {}),\n ...(attribution !== undefined ? { attribution } : {}),\n ...(locale !== undefined ? { locale } : {}),\n };\n}\n","import type { AiProvenance } from './types/ai.js';\nimport type { GraderUsage } from './types/grading.js';\n\n/**\n * How the SDK reads text a model wrote, before a learner does. Internal: shared\n * by the AI checks, and exported by none of the package's entry points.\n */\n\n/**\n * Plain text as the learner will read it: line endings unified, control\n * characters other than line breaks and tabs removed, runs of blank lines\n * collapsed, the ends trimmed.\n */\nexport function cleanText(text: string): string {\n return (\n text\n .replace(/\\r\\n?/g, '\\n')\n // biome-ignore lint/suspicious/noControlCharactersInRegex: removing them is the point\n .replace(/[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F\\u2028\\u2029]/g, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n );\n}\n\n/** A provenance field a host may send, kept only when it is a short string. */\nexport function provenanceOf(raw: unknown): AiProvenance | undefined {\n if (typeof raw !== 'object' || raw === null) {\n return undefined;\n }\n const kept: AiProvenance = {};\n for (const key of ['model', 'promptHash', 'generatedAt'] as const) {\n const value = (raw as Record<string, unknown>)[key];\n if (typeof value === 'string' && value.length > 0 && value.length <= 200) {\n kept[key] = value;\n }\n }\n return Object.keys(kept).length > 0 ? kept : undefined;\n}\n\n/**\n * What the call cost, as a port reported it. Read on the same terms as the\n * provenance: a number that is not finite and zero or more says nothing about\n * a cost, so it is dropped rather than carried into a host's totals.\n */\nexport function usageOf(raw: unknown): GraderUsage | undefined {\n if (typeof raw !== 'object' || raw === null) {\n return undefined;\n }\n const kept: GraderUsage = {};\n for (const key of ['promptTokens', 'completionTokens', 'costUsd'] as const) {\n const value = (raw as Record<string, unknown>)[key];\n if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {\n kept[key] = value;\n }\n }\n return Object.keys(kept).length > 0 ? kept : undefined;\n}\n\n/** Typographic quotes a model writes for the plain ones a learner typed, and back. */\nconst QUOTE_FOLD: Readonly<Record<string, string>> = {\n '‘': \"'\",\n '’': \"'\",\n '‛': \"'\",\n '′': \"'\",\n '“': '\"',\n '”': '\"',\n '„': '\"',\n '″': '\"',\n};\n\n/**\n * Text as a quote is matched against it: typographic quotes folded to plain\n * ones and every run of whitespace one space — the two liberties a model takes\n * when it copies words out — with, for each folded character, where it came\n * from, so a match maps back to the learner's own text. Case is kept: a\n * correction of \"i\" to \"I\" is about case.\n */\nexport function folded(text: string): { chars: string; from: number[] } {\n let chars = '';\n const from: number[] = [];\n let space = false;\n for (let at = 0; at < text.length; at += 1) {\n const char = text[at] as string;\n if (/\\s/u.test(char)) {\n if (!space) {\n chars += ' ';\n from.push(at);\n space = true;\n }\n continue;\n }\n space = false;\n chars += QUOTE_FOLD[char] ?? char;\n from.push(at);\n }\n return { chars, from };\n}\n\n/** A quote as it is searched for: folded, and without the spaces around it. */\nexport const needle = (quote: string): string => folded(quote).chars.trim();\n","import { materialFacts } from './ai-material.js';\nimport { cleanText, provenanceOf, usageOf } from './ai-text.js';\nimport { numberedPassage, placeholderOrder, revealsAnswer } from './answer-leak.js';\nimport { everyBuiltInTypeHandled } from './built-in-types.js';\nimport { alignDictation } from './scoring/dictation/align.js';\nimport { isGradeInRange } from './scoring/grade-numbers.js';\nimport { score } from './scoring/index.js';\nimport type {\n ActivityData,\n DictationData,\n FillInTheBlanksData,\n GapSelectData,\n ItemOutcome,\n LearnerResponse,\n MultipleChoiceData,\n ScoringDetail,\n} from './types/activity.js';\nimport type {\n AiActivityInput,\n AiExplanationRequest,\n AiFeature,\n AiGrade,\n AiGroupInput,\n AiHintRequest,\n AiItemFacts,\n AiRefusal,\n AiSupportedActivityType,\n AiTextResult,\n AiVerdict,\n AiWordFact,\n} from './types/ai.js';\n\n/**\n * The activity types the SDK builds facts for, and which of them take hints.\n * A dictation takes none: its answer is the whole sentence, and it has\n * word-by-word hints of its own.\n */\nexport const AI_EXPLANATION_TYPES: readonly AiSupportedActivityType[] = [\n 'multiple-choice',\n 'fill-in-the-blanks',\n 'gap-select',\n 'dictation',\n];\nexport const AI_HINT_TYPES: readonly AiSupportedActivityType[] = [\n 'multiple-choice',\n 'fill-in-the-blanks',\n 'gap-select',\n];\n\n/** The longest AI text shown, in code points. Longer is refused, not cut. */\nexport const AI_TEXT_MAX_LENGTH = 2000;\n\nexport { AI_MATERIAL_MAX_LENGTH } from './ai-material.js';\n\n/**\n * Whether the SDK supports `feature` for this activity type: explanations and\n * hints for the types it builds facts for, and writing feedback for a written\n * response, and coaching for a read-aloud. A consumer's own type has none.\n */\nexport function aiSupports(activityType: string, feature: AiFeature): boolean {\n if (feature === 'writing-feedback') {\n return activityType === 'written-response';\n }\n if (feature === 'pronunciation-coaching') {\n return activityType === 'read-aloud';\n }\n const types = feature === 'hint' ? AI_HINT_TYPES : AI_EXPLANATION_TYPES;\n return (types as readonly string[]).includes(activityType);\n}\n\n/**\n * Whether the item's author allows `feature`. Only an explicit `false`\n * switches it off: an author can refuse a feature, never force one on.\n *\n * Writing feedback and pronunciation coaching read `explanations`: each is a\n * model's words about the learner's own answer, and an author who switched one\n * off has switched off the others.\n */\nexport function aiAllowedByContent(data: AiActivityInput, feature: AiFeature): boolean {\n const permissions = (data as { ai?: unknown }).ai;\n if (typeof permissions !== 'object' || permissions === null) {\n return true;\n }\n const key = feature === 'hint' ? 'hints' : 'explanations';\n return (permissions as Record<string, unknown>)[key] !== false;\n}\n\n/** A grade as an explanation must speak to it: all marks, some, or none. */\nexport function aiGradeOf(result: { score: number; maxScore: number; passed: boolean }): AiGrade {\n const ratio = result.maxScore > 0 ? result.score / result.maxScore : 0;\n let category: AiVerdict = 'partly-correct';\n if (ratio >= 1 - 1e-9) {\n category = 'correct';\n } else if (ratio <= 1e-9) {\n category = 'incorrect';\n }\n return { score: result.score, maxScore: result.maxScore, passed: result.passed, category };\n}\n\n// ── Facts ──────────────────────────────────────────────────────────────\n\n/**\n * The `correct` flag a detail stored before 0.3 carries instead of an\n * `outcome`. On a blank, a gap or a word it meant the part was right; on a\n * multiple-choice option, that the learner acted rightly on it.\n */\nconst legacyCorrect = (detail: ScoringDetail): boolean =>\n (detail as { correct?: unknown }).correct === true;\n\n/** Whether a blank, a gap or a word was right, read from a server's detail. */\nfunction detailRight(detail: ScoringDetail): boolean {\n const outcome: unknown = detail.outcome;\n return outcome === undefined ? legacyCorrect(detail) : outcome === 'correct';\n}\n\n/** Whether an option is part of the answer, read from a server's detail (as the renderer reads it). */\nfunction isAnswerOption(detail: ScoringDetail, chosen: boolean): boolean {\n switch (detail.outcome) {\n case 'correct':\n case 'incorrect-omission':\n return true;\n case 'incorrect':\n case 'correct-omission':\n return false;\n default:\n return chosen ? legacyCorrect(detail) : !legacyCorrect(detail);\n }\n}\n\nconst firstString = (value: string | string[] | undefined): string | undefined =>\n Array.isArray(value) ? value[0] : value;\n\nconst listOf = (value: string | string[] | undefined): string[] | null =>\n value === undefined ? null : Array.isArray(value) ? [...value] : [value];\n\n/** The response a learner has before touching anything. */\nfunction emptyResponse(type: string): LearnerResponse | null {\n switch (type) {\n case 'multiple-choice':\n return { type: 'multiple-choice', selectedOptionIds: [] };\n case 'fill-in-the-blanks':\n return { type: 'fill-in-the-blanks', answers: {} };\n case 'gap-select':\n return { type: 'gap-select', selections: {} };\n case 'dictation':\n return { type: 'dictation', text: '' };\n default:\n return null;\n }\n}\n\nconst base = (data: AiActivityInput) => ({\n activityId: data.id,\n title: data.title,\n ...(typeof data.locale === 'string' ? { locale: data.locale } : {}),\n});\n\n/** The item's instructions, trimmed; nothing for none, or for blank ones the learner never sees. */\nfunction instructionsOf(data: AiActivityInput): { instructions?: string } {\n const text: unknown = (data as { instructions?: unknown }).instructions;\n return typeof text === 'string' && text.trim() !== '' ? { instructions: text.trim() } : {};\n}\n\n/**\n * The facts a model is given about one item and one answer. `details` are the\n * SDK's per-part verdicts — from its own scorer, or from a server's outcome in\n * review — and every `correct` is `null` without them. A redacted item yields\n * facts without a key, which is what the learner's side holds; a port that\n * needs the key loads the item on its server.\n *\n * `null` for a type the SDK builds no facts for.\n */\nexport function buildAiFacts(\n data: AiActivityInput,\n response: LearnerResponse | null,\n details: readonly ScoringDetail[] | null,\n): AiItemFacts | null {\n const byId = new Map((details ?? []).map((detail) => [detail.itemId, detail]));\n const full = (data as { redacted?: unknown }).redacted !== true;\n switch (data.type) {\n case 'multiple-choice': {\n const item = data as unknown as MultipleChoiceData;\n const chosen = new Set(\n response?.type === 'multiple-choice' ? response.selectedOptionIds : [],\n );\n return {\n ...base(data),\n activityType: 'multiple-choice',\n ...instructionsOf(data),\n question: item.question,\n mode: item.mode,\n options: item.options.map((option) => {\n const detail = byId.get(option.id);\n const wasChosen = chosen.has(option.id);\n let correct: boolean | null = null;\n if (full && typeof option.isCorrect === 'boolean') {\n correct = option.isCorrect;\n } else if (detail !== undefined) {\n correct = isAnswerOption(detail, wasChosen);\n }\n return {\n id: option.id,\n text: option.text,\n chosen: wasChosen,\n correct,\n ...(typeof option.feedback === 'string' ? { feedback: option.feedback } : {}),\n };\n }),\n };\n }\n case 'fill-in-the-blanks': {\n const item = data as unknown as FillInTheBlanksData;\n const answers = response?.type === 'fill-in-the-blanks' ? response.answers : {};\n const blanks = new Map(item.blanks.map((blank) => [blank.id, blank]));\n return {\n ...base(data),\n activityType: 'fill-in-the-blanks',\n ...instructionsOf(data),\n passage: numberedPassage(item.passage),\n blanks: placeholderOrder(item.passage).map((id, index) => {\n const blank = blanks.get(id);\n const detail = byId.get(id);\n const accepted =\n full && Array.isArray(blank?.acceptedAnswers)\n ? [...blank.acceptedAnswers]\n : listOf(detail?.correctResponse);\n return {\n id,\n position: index + 1,\n typed: typeof answers[id] === 'string' ? (answers[id] as string) : '',\n accepted,\n correct: detail === undefined ? null : detailRight(detail),\n ...(typeof blank?.hint === 'string' ? { hint: blank.hint } : {}),\n ...(typeof blank?.feedback === 'string' ? { feedback: blank.feedback } : {}),\n };\n }),\n };\n }\n case 'gap-select': {\n const item = data as unknown as GapSelectData;\n const selections = response?.type === 'gap-select' ? response.selections : {};\n const gaps = new Map(item.gaps.map((gap) => [gap.id, gap]));\n const banks = new Map((item.banks ?? []).map((bank) => [bank.id, bank]));\n return {\n ...base(data),\n activityType: 'gap-select',\n ...instructionsOf(data),\n passage: numberedPassage(item.passage),\n gaps: placeholderOrder(item.passage).map((id, index) => {\n const gap = gaps.get(id);\n const choices =\n gap?.choices ??\n (gap?.bankId === undefined ? [] : (banks.get(gap.bankId)?.choices ?? []));\n const textOf = (choiceId: string | undefined): string | null =>\n choiceId === undefined\n ? null\n : (choices.find((choice) => choice.id === choiceId)?.text ?? null);\n const detail = byId.get(id);\n const answerId =\n full && typeof gap?.correctChoiceId === 'string'\n ? gap.correctChoiceId\n : firstString(detail?.correctResponse);\n return {\n id,\n position: index + 1,\n choices: choices.map((choice) => choice.text),\n chosen: textOf(selections[id]),\n answer: textOf(answerId),\n correct: detail === undefined ? null : detailRight(detail),\n ...(typeof gap?.feedback === 'string' ? { feedback: gap.feedback } : {}),\n };\n }),\n };\n }\n case 'dictation': {\n const item = data as unknown as DictationData;\n const typed = response?.type === 'dictation' ? response.text : '';\n const hasKey = full && typeof item.transcript === 'string';\n let words: AiWordFact[] | null = null;\n if (hasKey) {\n words = alignDictation(item, typed).words.map((word) => ({\n expected: word.reference,\n typed: word.attempt,\n status: word.status,\n }));\n } else if (details !== null && details.length > 0) {\n words = details.map((detail) => {\n const expected = firstString(detail.correctResponse) ?? '';\n const attempt = firstString(detail.learnerResponse) ?? '';\n let status: 'correct' | 'incorrect' | 'missing' | 'extra' = 'incorrect';\n if (detailRight(detail)) {\n status = 'correct';\n } else if (attempt === '') {\n status = 'missing';\n } else if (expected === '') {\n status = 'extra';\n }\n return { expected, typed: attempt, status };\n });\n }\n return {\n ...base(data),\n activityType: 'dictation',\n transcript: hasKey ? item.transcript : null,\n typed,\n words,\n };\n }\n default:\n return null;\n }\n}\n\n/** The SDK's own grade of `response`, with its per-part details; `null` when it cannot grade. */\nfunction gradeLocally(\n data: AiActivityInput,\n response: LearnerResponse,\n): { details: ScoringDetail[]; grade: AiGrade } | null {\n if ((data as { redacted?: unknown }).redacted === true) {\n return null;\n }\n try {\n const result = score(data.type as never, data as unknown as ActivityData, response);\n return { details: result.details, grade: aiGradeOf(result) };\n } catch {\n return null;\n }\n}\n\n/**\n * The request for an explanation of `response`, or `null` when there is none\n * to give: an unsupported type, an author who switched explanations off,\n * nothing graded to explain, or a grade of record that cannot be a grade.\n *\n * A scored `outcome` — the grade of record a review shows — wins over grading\n * locally, so the explanation speaks to the grade on the screen. One whose\n * numbers cannot be a grade (the rule `outcomeFromGrade` applies: `maxScore`\n * positive and finite, `score` finite from 0 to it) gets no explanation at all:\n * explaining 85 \"out of 1\" would tell the model a wrong answer was correct, and\n * grading locally instead would speak to a grade the screen does not show.\n *\n * `group` is the item group the question sits in: its material — the passage,\n * or the instructions the group's questions share — goes with the request as\n * `material`, as the learner sees it.\n */\nexport function aiExplanationRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n outcome?: ItemOutcome;\n group?: AiGroupInput;\n learnerLocale?: string;\n}): AiExplanationRequest | null {\n const { data, response, outcome, learnerLocale } = input;\n if (!aiSupports(data.type, 'explanation') || !aiAllowedByContent(data, 'explanation')) {\n return null;\n }\n let details: ScoringDetail[] | null = null;\n let grade: AiGrade | null = null;\n if (outcome?.status === 'scored') {\n // Read once, so the check and the grade handed on see the same numbers.\n const { score: recorded, maxScore, passed } = outcome;\n if (!isGradeInRange(recorded, maxScore)) {\n return null;\n }\n details = outcome.details;\n grade = aiGradeOf({ score: recorded, maxScore, passed });\n } else if (response !== null) {\n const local = gradeLocally(data, response);\n if (local !== null) {\n details = local.details;\n grade = local.grade;\n }\n }\n if (grade === null) {\n return null;\n }\n const facts = buildAiFacts(data, response, details);\n if (facts === null) {\n return null;\n }\n const material = materialFacts(input.group);\n return {\n feature: 'explanation',\n facts,\n grade,\n ...(material !== null ? { material } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n };\n}\n\n/**\n * The request for the next hint on an unsubmitted answer, or `null` when\n * there is none to give: an unsupported type, an author who switched hints\n * off, or a redacted item — hints are for practice, where the item is whole.\n *\n * The facts carry the key, and which parts the learner has right so far, so a\n * model can hint where it helps. The learner never sees them; a hint that\n * contains an answer is refused by {@link checkAiHint}, whatever it quotes —\n * the group's material included.\n *\n * `group` is the item group the question sits in, as for\n * {@link aiExplanationRequest}.\n */\nexport function aiHintRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n previousHints: readonly string[];\n group?: AiGroupInput;\n learnerLocale?: string;\n}): AiHintRequest | null {\n const { data, previousHints, learnerLocale } = input;\n if (\n !aiSupports(data.type, 'hint') ||\n !aiAllowedByContent(data, 'hint') ||\n (data as { redacted?: unknown }).redacted === true\n ) {\n return null;\n }\n const response = input.response ?? emptyResponse(data.type);\n const local = response === null ? null : gradeLocally(data, response);\n const facts = buildAiFacts(data, response, local?.details ?? null);\n if (facts === null) {\n return null;\n }\n const material = materialFacts(input.group);\n return {\n feature: 'hint',\n facts,\n hintNumber: previousHints.length + 1,\n previousHints: [...previousHints],\n ...(material !== null ? { material } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n };\n}\n\n// ── Checking what comes back ───────────────────────────────────────────\n\nconst VERDICTS: readonly string[] = ['correct', 'partly-correct', 'incorrect'];\n\n/**\n * Reads what a port returned. Anything but an object with a string `text` is\n * `malformed`; text that is empty once cleaned, or longer than\n * {@link AI_TEXT_MAX_LENGTH}, is refused rather than shown or cut.\n */\nexport function readAiTextResult(\n raw: unknown,\n): { ok: true; result: AiTextResult } | { ok: false; refusal: AiRefusal } {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n return { ok: false, refusal: 'malformed' };\n }\n const record = raw as Record<string, unknown>;\n if (typeof record.text !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n if (record.verdict !== undefined && !VERDICTS.includes(record.verdict as string)) {\n return { ok: false, refusal: 'malformed' };\n }\n const text = cleanText(record.text);\n if (text === '') {\n return { ok: false, refusal: 'empty' };\n }\n if ([...text].length > AI_TEXT_MAX_LENGTH) {\n return { ok: false, refusal: 'too-long' };\n }\n const provenance = provenanceOf(record.provenance);\n const usage = usageOf(record.usage);\n return {\n ok: true,\n result: {\n text,\n ...(record.verdict !== undefined ? { verdict: record.verdict as AiVerdict } : {}),\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n\n/**\n * Checks an explanation a port returned for `request`. On top of\n * {@link readAiTextResult}: an explanation that states a verdict other than\n * the SDK's grade is refused (`contradicts-grade`) — it explains an answer\n * the learner did not get.\n */\nexport function checkAiExplanation(\n raw: unknown,\n request: AiExplanationRequest,\n): { ok: true; result: AiTextResult } | { ok: false; refusal: AiRefusal } {\n const read = readAiTextResult(raw);\n if (!read.ok) {\n return read;\n }\n if (read.result.verdict !== undefined && read.result.verdict !== request.grade.category) {\n return { ok: false, refusal: 'contradicts-grade' };\n }\n return read;\n}\n\n/**\n * Checks a hint a port returned for `request`. On top of\n * {@link readAiTextResult}: a hint that contains an answer is refused\n * (`reveals-answer`) — see {@link hintRevealsAnswer}.\n */\nexport function checkAiHint(\n raw: unknown,\n request: AiHintRequest,\n): { ok: true; result: AiTextResult } | { ok: false; refusal: AiRefusal } {\n const read = readAiTextResult(raw);\n if (!read.ok) {\n return read;\n }\n if (hintRevealsAnswer(request.facts, read.result.text)) {\n return { ok: false, refusal: 'reveals-answer' };\n }\n return read;\n}\n\n// ── The answer-leak guard ──────────────────────────────────────────────\n\n/**\n * Whether a hint gives away an answer, judged against the facts it was asked\n * for. It is a floor, not a proof: it finds an answer written out, not one\n * spelled letter by letter or described.\n *\n * - **Multiple choice:** the text of any correct option, as whole words.\n * - **Fill-in-the-blanks and gap select:** any accepted answer, as whole words.\n * An answer of one short word (\"is\", \"the\") counts only where the hint\n * writes it beside a neighbour it has in the passage — \"name is\", \"is\n * Rossi\" — so a hint may still use the word on its own.\n * - **Facts of any other kind:** revealing. A hint the check cannot read is\n * withheld, not shown unchecked.\n *\n * Case, accents and punctuation are ignored on both sides.\n */\nexport function hintRevealsAnswer(facts: AiItemFacts, hint: string): boolean {\n const reveals = (answer: string, passage?: string, position?: number): boolean =>\n revealsAnswer(hint, answer, passage, position);\n switch (facts.activityType) {\n case 'multiple-choice':\n return facts.options.some((option) => option.correct === true && reveals(option.text));\n case 'fill-in-the-blanks':\n return facts.blanks.some((blank) =>\n (blank.accepted ?? []).some((answer) => reveals(answer, facts.passage, blank.position)),\n );\n case 'gap-select':\n return facts.gaps.some(\n (gap) => gap.answer !== null && reveals(gap.answer, facts.passage, gap.position),\n );\n case 'dictation':\n // Never asked: a dictation takes no AI hints (AI_HINT_TYPES).\n return false;\n default:\n // Facts of a kind this check cannot read: the hint is withheld, not shown\n // unchecked. A new kind of facts does not compile until it has a case.\n everyBuiltInTypeHandled(facts);\n return true;\n }\n}\n","import { aiAllowedByContent, aiSupports, readAiTextResult } from './ai.js';\nimport { cleanText } from './ai-text.js';\nimport { alignReadAloud } from './scoring/speech/align.js';\nimport { validateSpeechAssessment } from './scoring/speech/assessment.js';\nimport type { ReadAloudData } from './types/activity.js';\nimport type {\n AiActivityInput,\n AiCoaching,\n AiCoachingRequest,\n AiReadingFacts,\n AiReadingWordFact,\n AiRefusal,\n AiSoundFact,\n} from './types/ai.js';\nimport type { GradeRecord } from './types/grading.js';\nimport type { SpeechAssessment } from './types/speech.js';\n\n/** The most words one coaching may work on. More is refused, not cut. */\nexport const AI_COACHING_MAX_WORDS = 20;\n\n/** The longest tip for one word, in code points. */\nexport const AI_COACHING_MAX_TIP_LENGTH = 500;\n\n/** The longest sound symbol: a phoneme, not a sentence. */\nconst MAX_SYMBOL_LENGTH = 16;\n\n/** The dimensions a reading is scored on, as the facts name them. */\nconst DIMENSIONS = ['accuracy', 'fluency', 'completeness', 'prosody'] as const;\n\n/** Two decimals: what a stored score out of 1 was, out of 100, without float noise. */\nconst hundredths = (value: number): number => Math.round(value * 10000) / 100;\n\nconst isFiniteNumber = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value);\n\n/** A word's sounds as the engine reported them: only those it names. */\nfunction soundsOf(word: SpeechAssessment['words'][number] | undefined): AiSoundFact[] | undefined {\n const sounds: AiSoundFact[] = [];\n for (const phoneme of word?.phonemes ?? []) {\n if (typeof phoneme.symbol !== 'string') {\n continue;\n }\n sounds.push({\n symbol: phoneme.symbol,\n ...(phoneme.accuracy !== undefined ? { accuracy: phoneme.accuracy } : {}),\n ...(phoneme.heardAs !== undefined && phoneme.heardAs.length > 0\n ? { heardAs: phoneme.heardAs.map(({ symbol, score }) => ({ symbol, score })) }\n : {}),\n });\n }\n return sounds.length > 0 ? sounds : undefined;\n}\n\n/** The facts from the engine's full assessment: every word's mark, and its sounds. */\nfunction fromAssessment(\n item: ReadAloudData,\n assessment: SpeechAssessment,\n): Pick<AiReadingFacts, 'assessor' | 'phonemeAlphabet' | 'scores' | 'words'> {\n const words: AiReadingWordFact[] = alignReadAloud(item, assessment).map((entry) => {\n const sounds =\n entry.itemId !== undefined && entry.wordIndex !== undefined\n ? soundsOf(assessment.words[entry.wordIndex])\n : undefined;\n return {\n ...(entry.itemId !== undefined ? { itemId: entry.itemId } : {}),\n word: entry.reference,\n heard: entry.heard,\n state: entry.state,\n ...(entry.accuracy !== undefined ? { accuracy: entry.accuracy } : {}),\n ...(sounds !== undefined ? { sounds } : {}),\n };\n });\n const scores: AiReadingFacts['scores'] = {};\n for (const dimension of DIMENSIONS) {\n const value = assessment.scores[dimension];\n if (value !== undefined) {\n scores[dimension] = value;\n }\n }\n return {\n assessor: assessment.assessor.kind,\n ...(assessment.phonemeAlphabet !== undefined\n ? { phonemeAlphabet: assessment.phonemeAlphabet }\n : {}),\n scores,\n words,\n };\n}\n\n/**\n * Whether an assessment holds marks on this text: one `validateSpeechAssessment`\n * accepts, of speech heard, made against the item's own text in its own locale\n * — the checks `gradeReadAloud` makes before it reads a mark. Anything else is\n * not marks on this reading, and would tell a model every word was left out.\n */\nconst marksThisText = (item: ReadAloudData, assessment: SpeechAssessment): boolean =>\n validateSpeechAssessment(assessment).success &&\n assessment.status === 'assessed' &&\n assessment.task === 'scripted' &&\n assessment.locale === item.locale &&\n assessment.referenceText === item.referenceText;\n\nconst STATE_OF_OUTCOME = {\n correct: 'correct',\n incorrect: 'mispronounced',\n 'incorrect-omission': 'omitted',\n} as const;\n\n/**\n * The facts from a stored grade alone: each word's mark and score, and no\n * sounds — a grade's details do not keep them.\n */\nfunction fromGrade(\n grade: GradeRecord,\n): Pick<AiReadingFacts, 'assessor' | 'scores' | 'words'> | null {\n if (!Array.isArray(grade.details) || grade.details.length === 0) {\n return null;\n }\n const words: AiReadingWordFact[] = [];\n for (const detail of grade.details) {\n const state = STATE_OF_OUTCOME[detail.outcome as keyof typeof STATE_OF_OUTCOME];\n if (state === undefined || typeof detail.itemId !== 'string') {\n continue;\n }\n const text = (value: string | string[]): string =>\n Array.isArray(value) ? value.join(' ') : value;\n words.push({\n itemId: detail.itemId,\n word: text(detail.correctResponse),\n heard: text(detail.learnerResponse),\n state,\n ...(state !== 'omitted' && isFiniteNumber(detail.score)\n ? { accuracy: hundredths(detail.score) }\n : {}),\n });\n }\n if (words.length === 0) {\n return null;\n }\n // Each dimension out of 100, whatever scale the grade kept it on. A criterion\n // without a scale to read it on is left out rather than given one.\n const scores: AiReadingFacts['scores'] = {};\n for (const criterion of grade.criteria ?? []) {\n const dimension = DIMENSIONS.find((name) => name === criterion.name);\n if (\n dimension !== undefined &&\n isFiniteNumber(criterion.score) &&\n isFiniteNumber(criterion.maxScore) &&\n criterion.maxScore > 0\n ) {\n scores[dimension] = hundredths(criterion.score / criterion.maxScore);\n }\n }\n const kind = grade.grader?.kind;\n return {\n assessor: kind === 'auto' || kind === 'ai' || kind === 'human' ? kind : 'unknown',\n scores,\n words,\n };\n}\n\n/**\n * The request for coaching on a graded reading, or `null` when there is none\n * to give: not a read-aloud, an author who switched explanations off (coaching\n * reads `ai.explanations`), or no marks.\n *\n * The marks come from the `assessment` when one is given — with each word's\n * sounds and what each was heard as — and then only from it: an assessment\n * `gradeReadAloud` would not read (no speech heard, unscripted, or made against\n * another text or locale) gives none, rather than a stored grade's marks the\n * learner is not looking at. With no assessment, they come from\n * `grade.details`, without sounds.\n *\n * The facts are the text as the learner saw it, those marks word by word, and\n * the engine's dimension scores. The marks are the engine's; the model\n * explains them.\n */\nexport function aiCoachingRequest(input: {\n data: AiActivityInput;\n assessment?: SpeechAssessment | null;\n grade?: GradeRecord | null;\n learnerLocale?: string;\n}): AiCoachingRequest | null {\n const { data, assessment, grade, learnerLocale } = input;\n if (\n !aiSupports(data.type, 'pronunciation-coaching') ||\n !aiAllowedByContent(data, 'pronunciation-coaching')\n ) {\n return null;\n }\n const item = data as unknown as ReadAloudData;\n if (typeof item.referenceText !== 'string' || typeof item.locale !== 'string') {\n return null;\n }\n // An assessment given is the marks, and the only ones: a learner looking at\n // it is not looking at a stored grade's, and coaching must be about what is\n // on screen.\n const marks =\n assessment !== undefined && assessment !== null\n ? marksThisText(item, assessment)\n ? fromAssessment(item, assessment)\n : null\n : grade !== undefined && grade !== null\n ? fromGrade(grade)\n : null;\n if (marks === null) {\n return null;\n }\n const graded =\n grade !== undefined &&\n grade !== null &&\n isFiniteNumber(grade.score) &&\n isFiniteNumber(grade.maxScore) &&\n typeof grade.passed === 'boolean'\n ? { score: grade.score, maxScore: grade.maxScore, passed: grade.passed }\n : undefined;\n return {\n feature: 'pronunciation-coaching',\n facts: {\n activityType: 'read-aloud',\n activityId: data.id,\n title: data.title,\n locale: item.locale,\n referenceText: item.referenceText,\n ...(typeof item.instructions === 'string' ? { instructions: item.instructions } : {}),\n ...marks,\n },\n ...(graded !== undefined ? { grade: graded } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n };\n}\n\n// ── Checking what comes back ───────────────────────────────────────────\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/** A symbol as it is compared: composed, and without the spaces around it. */\nconst symbolKey = (symbol: string): string => symbol.normalize('NFC').trim();\n\ntype Refused = { ok: false; refusal: AiRefusal };\n\n/**\n * Checks coaching a port returned for `request`, and turns it into what a\n * learner is shown. As every AI text, it is shown whole or not at all.\n *\n * - **The text** is read as every AI text is (`readAiTextResult`).\n * - **Every word coached is one the engine marked.** Each `itemId` names a word\n * of the text marked `mispronounced` or `omitted`, once; coaching on a word\n * read correctly, or one the text does not have, refuses the whole reply as\n * `contradicts-marks`. Praise belongs in `text`.\n * - **Every sound named is one the engine reported** for that word, and a sound\n * it was heard as is one the engine heard it as. Where the facts carry no\n * sounds — marks from a stored grade, or an assessor that reports none — any\n * sound is `contradicts-marks`: nothing stands behind it.\n * - **Nothing numeric is read.** A score in the reply is ignored: the marks and\n * the grade are the engine's.\n * - **Limits:** {@link AI_COACHING_MAX_WORDS} words, a tip of\n * {@link AI_COACHING_MAX_TIP_LENGTH} code points; over them `too-long`. A\n * shape that is not the one documented is `malformed`.\n *\n * The words come back in reading order, each with the word as the marks show it\n * (the SDK's reading of the text, never the model's), and each sound spelt as\n * the engine spelt it.\n */\nexport function checkAiCoaching(\n raw: unknown,\n request: AiCoachingRequest,\n): { ok: true; coaching: AiCoaching } | { ok: false; refusal: AiRefusal } {\n const read = readAiTextResult(raw);\n if (!read.ok) {\n return read;\n }\n const entries = (raw as Record<string, unknown>).words;\n const words: (AiCoaching['words'][number] & { order: number })[] = [];\n if (entries !== undefined && entries !== null) {\n if (!Array.isArray(entries)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (entries.length > AI_COACHING_MAX_WORDS) {\n return { ok: false, refusal: 'too-long' };\n }\n const facts = new Map<string, { fact: AiReadingWordFact; order: number }>();\n request.facts.words.forEach((fact, order) => {\n if (fact.itemId !== undefined) {\n facts.set(fact.itemId, { fact, order });\n }\n });\n const seen = new Set<string>();\n for (const entry of entries) {\n if (!isRecord(entry) || typeof entry.itemId !== 'string' || typeof entry.tip !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n const tip = cleanText(entry.tip);\n if (tip === '') {\n return { ok: false, refusal: 'malformed' };\n }\n if ([...tip].length > AI_COACHING_MAX_TIP_LENGTH) {\n return { ok: false, refusal: 'too-long' };\n }\n if (seen.has(entry.itemId)) {\n return { ok: false, refusal: 'malformed' };\n }\n seen.add(entry.itemId);\n const found = facts.get(entry.itemId);\n if (\n found === undefined ||\n (found.fact.state !== 'mispronounced' && found.fact.state !== 'omitted')\n ) {\n return { ok: false, refusal: 'contradicts-marks' };\n }\n const sound = checkSound(entry.sound, found.fact);\n if (sound !== undefined && 'refusal' in sound) {\n return sound;\n }\n words.push({\n itemId: entry.itemId,\n word: found.fact.word,\n tip,\n ...(sound !== undefined ? { sound } : {}),\n order: found.order,\n });\n }\n }\n const { text, provenance, usage } = read.result;\n return {\n ok: true,\n coaching: {\n text,\n words: words.sort((a, b) => a.order - b.order).map(({ order: _order, ...word }) => word),\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n\n/** A sound a model names, checked against the sounds the engine reported for the word. */\nfunction checkSound(\n raw: unknown,\n fact: AiReadingWordFact,\n): { expected: string; heard?: string } | Refused | undefined {\n if (raw === undefined || raw === null) {\n return undefined;\n }\n if (\n !isRecord(raw) ||\n typeof raw.expected !== 'string' ||\n (raw.heard !== undefined && raw.heard !== null && typeof raw.heard !== 'string')\n ) {\n return { ok: false, refusal: 'malformed' };\n }\n const heard = typeof raw.heard === 'string' ? raw.heard : undefined;\n if (\n [...raw.expected].length > MAX_SYMBOL_LENGTH ||\n (heard !== undefined && [...heard].length > MAX_SYMBOL_LENGTH)\n ) {\n return { ok: false, refusal: 'too-long' };\n }\n const expected = symbolKey(raw.expected);\n const reported = fact.sounds?.find((sound) => symbolKey(sound.symbol) === expected);\n if (reported === undefined) {\n return { ok: false, refusal: 'contradicts-marks' };\n }\n if (heard === undefined) {\n return { expected: reported.symbol };\n }\n const instead = reported.heardAs?.find(\n (candidate) => symbolKey(candidate.symbol) === symbolKey(heard),\n );\n if (instead === undefined) {\n return { ok: false, refusal: 'contradicts-marks' };\n }\n return { expected: reported.symbol, heard: instead.symbol };\n}\n","import { getActivityTypeDescriptor, type MediaSlot } from './registry/index.js';\n\n/**\n * What a media reference points at. The first four are a holder's main file,\n * named by its `type`; the rest are the files beside it.\n */\nexport type MediaRefKind = 'audio' | 'video' | 'image' | 'embed' | 'captions' | 'track' | 'poster';\n\n/** One address in a piece of content, with where it sits. See {@link listMediaRefs}. */\nexport interface MediaRef {\n /**\n * From the entry to the field holding the address, e.g.\n * `['items', 2, 'options', 1, 'media', 'url']`. Authored positions, so a\n * shuffle never moves it.\n */\n readonly path: readonly (string | number)[];\n readonly kind: MediaRefKind;\n /**\n * The address. `null` for a holder's main file that has none yet: a draft\n * whose recording is still to be made, as a parallel item's is.\n */\n readonly url: string | null;\n /**\n * The object holding the address, as stored and never to be changed: the\n * media, an option's media, a slow recording, or a caption track. A\n * recording's `transcript` and `script` are read from it.\n */\n readonly holder: Readonly<Record<string, unknown>>;\n /** The activity it belongs to. Absent for a group's stimulus. */\n readonly activityId?: string;\n /** The stimulus it belongs to, for a group's material. */\n readonly stimulusId?: string;\n /** A `track`'s language. */\n readonly srclang?: string;\n}\n\nconst MAIN_KINDS: readonly string[] = ['audio', 'video', 'image', 'embed'];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction valueAt(root: unknown, path: readonly (string | number)[]): unknown {\n let at = root;\n for (const key of path) {\n if (typeof key === 'number' ? !Array.isArray(at) : !isRecord(at)) {\n return undefined;\n }\n at = (at as Record<string | number, unknown>)[key];\n }\n return at;\n}\n\n/** Where `item` keeps its files: what its type declares, or its `media` alone. */\nfunction slotsOf(item: Record<string, unknown>): readonly MediaSlot[] {\n const declared =\n typeof item.type === 'string' ? getActivityTypeDescriptor(item.type)?.mediaSlots : undefined;\n if (declared === undefined) {\n return [{ path: ['media'] }];\n }\n const slots = declared(item);\n return Array.isArray(slots) ? slots : [];\n}\n\ninterface Owner {\n activityId?: string;\n stimulusId?: string;\n}\n\nfunction holderRefs(\n holder: unknown,\n path: readonly (string | number)[],\n kindOfSlot: MediaSlot['kind'],\n owner: Owner,\n): MediaRef[] {\n if (!isRecord(holder)) {\n return [];\n }\n const own = typeof holder.type === 'string' && MAIN_KINDS.includes(holder.type);\n const main = (own ? holder.type : kindOfSlot) as MediaRefKind | undefined;\n const refs: MediaRef[] = [];\n const add = (field: readonly (string | number)[], kind: MediaRefKind, url: string | null) => {\n refs.push({ path: [...path, ...field], kind, url, holder, ...owner });\n };\n // A holder that names no kind of file is not valid content, and its file\n // could not be named; it is left out rather than guessed at.\n if (main !== undefined) {\n add(['url'], main, typeof holder.url === 'string' ? holder.url : null);\n }\n if (typeof holder.captionsUrl === 'string') {\n add(['captionsUrl'], 'captions', holder.captionsUrl);\n }\n if (Array.isArray(holder.tracks)) {\n holder.tracks.forEach((track, index) => {\n if (isRecord(track) && typeof track.src === 'string') {\n refs.push({\n path: [...path, 'tracks', index, 'src'],\n kind: 'track',\n url: track.src,\n holder: track,\n ...owner,\n ...(typeof track.srclang === 'string' ? { srclang: track.srclang } : {}),\n });\n }\n });\n }\n if (typeof holder.poster === 'string') {\n add(['poster'], 'poster', holder.poster);\n }\n return refs;\n}\n\nfunction activityRefs(item: unknown, base: readonly (string | number)[]): MediaRef[] {\n if (!isRecord(item)) {\n return [];\n }\n const owner: Owner = typeof item.id === 'string' ? { activityId: item.id } : {};\n return slotsOf(item).flatMap((slot) =>\n holderRefs(valueAt(item, slot.path), [...base, ...slot.path], slot.kind, owner),\n );\n}\n\n/**\n * Every media address in one piece of content: a loose activity or an item\n * group (its stimulus first, then each item in authored order), as a\n * `SequenceEntry` holds them. Drafts are read too.\n *\n * For each activity the walker reads the places its type keeps files: an\n * item's `media`, each multiple-choice option's `media`, and dictation's and\n * read-aloud's `slowMedia`. In each it lists the main file, `captionsUrl`,\n * every track's `src` and `poster`. A type you register declares its own with\n * `mediaSlots`; without that, its `media` is read.\n *\n * A main file with no address yet is listed with `url: null`, so a recording\n * still to be made has a place in the list. Not read: addresses inside an\n * `…Html` sidecar (the SDK does not parse HTML) and a learner's recordings,\n * which are responses rather than content.\n *\n * ```ts\n * const files = listMediaRefs(entry).filter((ref) => ref.url !== null);\n * ```\n */\nexport function listMediaRefs(entry: unknown): MediaRef[] {\n if (!isRecord(entry)) {\n return [];\n }\n if (entry.type !== 'item-group') {\n return activityRefs(entry, []);\n }\n const refs: MediaRef[] = [];\n const stimulus = entry.stimulus;\n if (isRecord(stimulus)) {\n const owner: Owner = typeof stimulus.id === 'string' ? { stimulusId: stimulus.id } : {};\n refs.push(...holderRefs(stimulus.media, ['stimulus', 'media'], undefined, owner));\n }\n if (Array.isArray(entry.items)) {\n entry.items.forEach((item, index) => {\n refs.push(...activityRefs(item, ['items', index]));\n });\n }\n return refs;\n}\n\n/** A deep copy of a JSON value. Content is plain data; a cycle cannot be copied. */\nfunction copyOf(value: unknown, seen: Set<object>): unknown {\n if (typeof value !== 'object' || value === null) {\n return value;\n }\n if (seen.has(value)) {\n throw new TypeError('mapMediaRefs: the content contains a circular reference.');\n }\n seen.add(value);\n try {\n if (Array.isArray(value)) {\n return value.map((element) => copyOf(element, seen));\n }\n const copy: Record<string, unknown> = {};\n for (const [key, field] of Object.entries(value)) {\n copy[key] = copyOf(field, seen);\n }\n return copy;\n } finally {\n seen.delete(value);\n }\n}\n\n/**\n * A copy of `entry` with each address {@link listMediaRefs} finds replaced\n * by what `fn` returns for it: files moved into a new folder structure, or an\n * export's file names pointed at where an import stored them. Nothing else\n * changes, and `entry` is never modified: with `(ref) => ref.url`, the copy is\n * deep-equal to it, with the same `canonicalJson`.\n *\n * For a main file with no address yet (`url: null`), a string attaches one\n * and `null` leaves it empty. For an address that is there, `fn` must return\n * a string: removing a file is not mapping.\n *\n * The copy validates as the original did when every address `fn` returns is\n * one the media rule accepts: `https:`, `http:`, `data:`, `blob:`, or\n * root-relative (`/…`). A bundle-relative `media/part1.mp3` is refused.\n *\n * @throws TypeError when `fn` returns something other than a string or\n * `null`, or `null` for an address that is there, or when `entry` is circular.\n */\nexport function mapMediaRefs<T>(entry: T, fn: (ref: MediaRef) => string | null): T {\n const refs = listMediaRefs(entry);\n const copy = copyOf(entry, new Set()) as T;\n for (const ref of refs) {\n const next = fn(ref);\n if (next === null) {\n if (ref.url !== null) {\n throw new TypeError(\n `mapMediaRefs: ${ref.path.join('.')} has a file; returning null would remove it, which is not mapping.`,\n );\n }\n continue;\n }\n if (typeof next !== 'string') {\n throw new TypeError(\n `mapMediaRefs: the function must return a string or null for ${ref.path.join('.')}, not ${typeof next}.`,\n );\n }\n if (next === ref.url) {\n continue;\n }\n const holder = valueAt(copy, ref.path.slice(0, -1)) as Record<string | number, unknown>;\n holder[ref.path[ref.path.length - 1] as string | number] = next;\n }\n return copy;\n}\n\n/** A holder's own text: its transcript, else its script as \"Speaker: text\" lines. */\nfunction ownText(holder: Record<string, unknown>): string | null {\n if (typeof holder.transcript === 'string' && holder.transcript.trim() !== '') {\n return holder.transcript;\n }\n const turns = Array.isArray(holder.script)\n ? holder.script.flatMap((turn) =>\n isRecord(turn) &&\n typeof turn.speaker === 'string' &&\n typeof turn.text === 'string' &&\n turn.text.trim() !== ''\n ? [`${turn.speaker.trim()}: ${turn.text.trim()}`]\n : [],\n )\n : [];\n return turns.length > 0 ? turns.join('\\n') : null;\n}\n\n/**\n * The author-side text of a recording: its `transcript`, or else its\n * `script` as one \"Speaker: text\" line per turn. `null` when it has neither.\n *\n * `holder` is a media object, a stimulus, or an activity. A stimulus or an\n * activity is read for its own text first and then for its `media`'s; a\n * stimulus keeps both at its own level. Author-only: pass it to the author's\n * requests (`aiDraftsRequest`'s `source`, a critique), never to a learner's.\n *\n * ```ts\n * const text = transcriptOf(item.media);\n * if (text !== null) aiDraftsRequest({ types: ['multiple-choice'], source: { kind: 'transcript', text } });\n * ```\n */\nexport function transcriptOf(holder: unknown): string | null {\n if (!isRecord(holder)) {\n return null;\n }\n return ownText(holder) ?? (isRecord(holder.media) ? ownText(holder.media) : null);\n}\n","import { fold } from '../answer-leak.js';\nimport { listMediaRefs } from '../media-refs.js';\nimport type { ItemFinding } from '../types/authoring.js';\nimport { finding } from './findings.js';\nimport { isRecord } from './issues.js';\n\n/**\n * The critic's rules for what a recording says: its author-only `transcript`\n * and `script`, wherever media sits. Words are compared as the other rules\n * compare them (`fold`: case, accents and punctuation ignored), and a\n * speaker's label is not one of a recording's words.\n */\n\ntype Path = readonly (string | number)[];\n\nconst written = (value: unknown): value is string =>\n typeof value === 'string' && value.trim() !== '';\n\n/** The script's turns that have a speaker and words, or none. */\nfunction turnsOf(script: unknown): { speaker: string; text: string }[] {\n return Array.isArray(script)\n ? script.flatMap((turn) =>\n isRecord(turn) && typeof turn.speaker === 'string' && written(turn.text)\n ? [{ speaker: turn.speaker, text: turn.text }]\n : [],\n )\n : [];\n}\n\nconst escaped = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n/** A transcript's words, with each `Speaker:` label of the script's speakers taken out. */\nfunction transcriptWords(transcript: string, speakers: readonly string[]): string[] {\n const labels = [...new Set(speakers.map((speaker) => speaker.trim()).filter(written))];\n const unlabelled =\n labels.length === 0\n ? transcript\n : transcript.replace(\n new RegExp(`(^|\\\\s)(?:${labels.map(escaped).join('|')})\\\\s*:`, 'giu'),\n '$1',\n );\n return fold(unlabelled);\n}\n\n/** What a recording says, as words: its transcript, or else its script's turns. */\nfunction spokenWords(holder: Record<string, unknown>): string[] | null {\n const turns = turnsOf(holder.script);\n if (written(holder.transcript)) {\n return transcriptWords(\n holder.transcript,\n turns.map((turn) => turn.speaker),\n );\n }\n return turns.length > 0 ? fold(turns.map((turn) => turn.text).join(' ')) : null;\n}\n\nconst sameWords = (a: readonly string[], b: readonly string[]) =>\n a.length === b.length && a.every((word, index) => word === b[index]);\n\n/** One object's own rules: text on media with nothing to say, and a transcript its script contradicts. */\nfunction holderFindings(holder: unknown, path: Path): ItemFinding[] {\n if (!isRecord(holder)) {\n return [];\n }\n const findings: ItemFinding[] = [];\n const turns = turnsOf(holder.script);\n const hasText = written(holder.transcript) || turns.length > 0;\n if (hasText && (holder.type === 'image' || holder.type === 'embed')) {\n findings.push(\n finding(\n 'media_transcript_unused',\n [...path, written(holder.transcript) ? 'transcript' : 'script'],\n `A transcript or a script on ${holder.type === 'image' ? 'a picture' : 'an embedded player'} has nothing to describe, and nothing reads it. Put it on the recording it belongs to.`,\n ),\n );\n }\n if (written(holder.transcript) && turns.length > 0) {\n const fromTranscript = transcriptWords(\n holder.transcript,\n turns.map((turn) => turn.speaker),\n );\n if (!sameWords(fromTranscript, fold(turns.map((turn) => turn.text).join(' ')))) {\n findings.push(\n finding(\n 'media_script_differs',\n [...path, 'script'],\n 'The script and the transcript say different words. AI facts read the transcript, and a voice reads the script: make them say the same.',\n ),\n );\n }\n }\n return findings;\n}\n\n/** The field a type's own recording reads out, for the types that have one. */\nconst SPOKEN_FIELD: Readonly<Record<string, string>> = {\n dictation: 'transcript',\n 'read-aloud': 'referenceText',\n};\n\n/**\n * An item's recording rules, at every place its type keeps media. A\n * dictation's and a read-aloud's own text is what their recording says, so a\n * recording that says other words is reported too.\n */\nexport function recordingFindings(item: Record<string, unknown>): ItemFinding[] {\n // Each media object once: the walker lists its main file first, at `<path>.url`.\n const findings = listMediaRefs(item)\n .filter((ref) => ref.path[ref.path.length - 1] === 'url')\n .flatMap((ref) => holderFindings(ref.holder, ref.path.slice(0, -1)));\n const field = typeof item.type === 'string' ? SPOKEN_FIELD[item.type] : undefined;\n const media = item.media;\n if (field === undefined || !isRecord(media)) {\n return findings;\n }\n const said = spokenWords(media);\n const accepted = [\n item[field],\n ...(item.type === 'dictation' && Array.isArray(item.acceptedTranscripts)\n ? item.acceptedTranscripts\n : []),\n ].filter(written);\n if (\n said !== null &&\n accepted.length > 0 &&\n !accepted.some((text) => sameWords(said, fold(text)))\n ) {\n findings.push(\n finding(\n 'media_transcript_differs',\n ['media', written(media.transcript) ? 'transcript' : 'script'],\n `The recording's transcript says different words from the item's \\`${field}\\`, which is what the recording is meant to say.`,\n ),\n );\n }\n return findings;\n}\n\n/** A group's material: its own transcript against its script, and its media's rules. */\nexport function stimulusFindings(stimulus: unknown): ItemFinding[] {\n if (!isRecord(stimulus)) {\n return [];\n }\n return [\n ...holderFindings({ transcript: stimulus.transcript, script: stimulus.script }, ['stimulus']),\n ...holderFindings(stimulus.media, ['stimulus', 'media']),\n ];\n}\n","import { fold, passageWithoutPlaceholders, placeholderOrder } from './answer-leak.js';\nimport { canonicalJson } from './content-hash.js';\nimport { policyFor } from './scoring/activity-scorers/fill-in-the-blanks.js';\nimport type { BlankConfig } from './types/activity.js';\n\n/**\n * What makes two items parallel, as rules: their structure, the length of what\n * a learner reads or hears, whether one copies the other, and whether they\n * share a right answer. One module, read by the variants a model writes\n * (`checkAiVariants`), by the paper critic (`critiqueDrafts`) and by the\n * comparison of two forms (`critiqueVersions`), so the three never disagree.\n *\n * Every rule reads content as stored, finished or not: it looks only at the\n * fields that are there and never throws.\n */\n\ntype Fields = Readonly<Record<string, unknown>>;\n\nconst isRecord = (value: unknown): value is Fields =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\nconst listOf = (value: unknown): readonly unknown[] => (Array.isArray(value) ? value : []);\nconst stringOf = (value: unknown): string => (typeof value === 'string' ? value : '');\nconst same = (a: unknown, b: unknown): boolean =>\n canonicalJson(a ?? null) === canonicalJson(b ?? null);\n\n/**\n * A passage's blanks or gaps in the order its placeholders appear, each with\n * its index in the list: the order a learner meets them, which is how two\n * items are compared and how a model writes them. The list's own order where\n * the passage does not name each of them once.\n */\nexport function inPassageOrder(\n item: Fields,\n field: 'blanks' | 'gaps',\n): { at: number; value: unknown }[] {\n const list = listOf(item[field]);\n const order = typeof item.passage === 'string' ? placeholderOrder(item.passage) : [];\n const found = order.map((id) => list.findIndex((one) => isRecord(one) && one.id === id));\n if (\n found.length === list.length &&\n found.every((at) => at !== -1) &&\n new Set(found).size === found.length\n ) {\n return found.map((at) => ({ at, value: list[at] }));\n }\n return list.map((value, at) => ({ at, value }));\n}\n\n// ── Similarity ─────────────────────────────────────────────────────────\n\n/**\n * How alike two texts must be to count as one copying the other: the share of\n * their word triples (3-word shingles) they have in common, as Jaccard\n * similarity. 0.5 means half of everything either text says, three words at a\n * time, is in the other.\n */\nexport const AI_VARIANT_NEAR_COPY_THRESHOLD = 0.5;\n\n/** Texts shorter than this, in words, are compared whole: word triples say nothing about them. */\nconst SHORT_TEXT_WORDS = 6;\n\n/**\n * The words of a text as every comparison here reads them: placeholders\n * (`{{1}}`) taken out, compatibility forms read as what they stand for, accents\n * and case ignored, and punctuation — typographic quotes included — gone.\n */\nexport function wordsOf(text: string): string[] {\n return fold(passageWithoutPlaceholders(text));\n}\n\nfunction triples(words: readonly string[]): Set<string> {\n const found = new Set<string>();\n for (let at = 0; at + 3 <= words.length; at += 1) {\n found.add(`${words[at]} ${words[at + 1]} ${words[at + 2]}`);\n }\n return found;\n}\n\n/**\n * How alike two texts are, from 0 to 1: the Jaccard similarity of their word\n * triples, or — when either has fewer than six words — 1 if they read the same\n * and 0 if not. A text with no words is like nothing.\n */\nexport function textSimilarity(a: string, b: string): number {\n const left = wordsOf(a);\n const right = wordsOf(b);\n if (left.length === 0 || right.length === 0) {\n return 0;\n }\n if (left.length < SHORT_TEXT_WORDS || right.length < SHORT_TEXT_WORDS) {\n return left.join(' ') === right.join(' ') ? 1 : 0;\n }\n const one = triples(left);\n const two = triples(right);\n let shared = 0;\n for (const triple of one) {\n if (two.has(triple)) {\n shared += 1;\n }\n }\n return shared / (one.size + two.size - shared);\n}\n\n/** A similarity as a message shows it: two decimals. */\nexport const shownScore = (score: number): string => score.toFixed(2);\n\n// ── The texts of an item ───────────────────────────────────────────────\n\n/**\n * What a text is, for comparing it with another: a question's own words\n * (`stem`: a question, a passage with gaps, a writing task), words a learner\n * hears or says (`spoken`: a dictation's transcript, a read-aloud's text), or\n * the material questions are about (`material`: a passage, a listening\n * script, what a recording says). Texts are compared with texts of their kind.\n */\nexport type TextKind = 'stem' | 'spoken' | 'material';\n\nexport interface ItemText {\n /** Where the text is, from the entry. */\n path: string[];\n kind: TextKind;\n /** Which length band applies: the material's, or an item text's. */\n band: 'material' | 'item';\n text: string;\n}\n\nconst RECORDING_TYPES: readonly string[] = ['audio', 'video', 'embed'];\n\n/** A recording's words: its script's turns, speakers left out, else its transcript. */\nfunction recordingText(holder: Fields): { field: 'script' | 'transcript'; text: string } | null {\n const turns = listOf(holder.script).flatMap((turn) =>\n isRecord(turn) && typeof turn.text === 'string' && turn.text.trim() !== '' ? [turn.text] : [],\n );\n if (turns.length > 0) {\n return { field: 'script', text: turns.join('\\n') };\n }\n return typeof holder.transcript === 'string' && holder.transcript.trim() !== ''\n ? { field: 'transcript', text: holder.transcript }\n : null;\n}\n\nfunction mediaTexts(media: unknown, at: readonly string[]): ItemText[] {\n if (!isRecord(media) || !RECORDING_TYPES.includes(stringOf(media.type))) {\n return [];\n }\n const said = recordingText(media);\n return said === null\n ? []\n : [{ path: [...at, said.field], kind: 'material', band: 'material', text: said.text }];\n}\n\n/** The fields an activity type keeps its own text in, and what each is. */\nconst OWN_TEXT: Readonly<Record<string, { field: string; kind: TextKind }>> = {\n 'multiple-choice': { field: 'question', kind: 'stem' },\n 'fill-in-the-blanks': { field: 'passage', kind: 'stem' },\n 'gap-select': { field: 'passage', kind: 'stem' },\n 'written-response': { field: 'prompt', kind: 'stem' },\n dictation: { field: 'transcript', kind: 'spoken' },\n 'read-aloud': { field: 'referenceText', kind: 'spoken' },\n};\n\n/**\n * Every text of an entry a near copy or a length is judged on: an item's own\n * text and what its recording says; for an item group, its stimulus's passage\n * and recording, then each item's under `items.N`. A dictation's or a\n * read-aloud's recording says the item's own text, so it is not read twice.\n */\nexport function textsOf(entry: unknown): ItemText[] {\n if (!isRecord(entry)) {\n return [];\n }\n if (entry.type === 'item-group') {\n const stimulus = isRecord(entry.stimulus) ? entry.stimulus : {};\n const own = recordingText(stimulus);\n return [\n ...(typeof stimulus.body === 'string' && stimulus.body.trim() !== ''\n ? [\n {\n path: ['stimulus', 'body'],\n kind: 'material' as const,\n band: 'material' as const,\n text: stimulus.body,\n },\n ]\n : []),\n ...(own !== null\n ? [\n {\n path: ['stimulus', own.field],\n kind: 'material' as const,\n band: 'material' as const,\n text: own.text,\n },\n ]\n : mediaTexts(stimulus.media, ['stimulus', 'media'])),\n ...listOf(entry.items).flatMap((item, index) =>\n textsOf(item).map((found) => ({ ...found, path: ['items', String(index), ...found.path] })),\n ),\n ];\n }\n const own = OWN_TEXT[stringOf(entry.type)];\n if (own === undefined) {\n return [];\n }\n const text = entry[own.field];\n return [\n ...(typeof text === 'string' && text.trim() !== ''\n ? [{ path: [own.field], kind: own.kind, band: 'item' as const, text }]\n : []),\n ...(own.kind === 'spoken' ? [] : mediaTexts(entry.media, ['media'])),\n ];\n}\n\n// ── Keys ───────────────────────────────────────────────────────────────\n\n/** A key as compared: folded, and kept in its case when its blank matches case. */\nexport function keyOf(text: string, caseSensitive = false): string {\n if (!caseSensitive) {\n return fold(text).join(' ');\n }\n return text\n .normalize('NFKD')\n .replace(/\\p{M}/gu, '')\n .replace(/[^\\p{L}\\p{N}]+/gu, ' ')\n .trim();\n}\n\n/**\n * One place an item keeps a right answer: a multiple choice's correct options\n * (`options`), a blank (`blanks.N`) or a gap (`gaps.N`). `answers` are the\n * keys as compared, `shown` the first as written.\n */\nexport interface KeySlot {\n slot: string;\n path: string[];\n answers: string[];\n shown: string;\n}\n\nfunction choiceText(item: Fields, gap: Fields): string | undefined {\n const own = listOf(gap.choices);\n const bank = listOf(item.banks).find((one) => isRecord(one) && one.id === gap.bankId);\n const choices = own.length > 0 ? own : isRecord(bank) ? listOf(bank.choices) : [];\n const right = choices.find((choice) => isRecord(choice) && choice.id === gap.correctChoiceId);\n return isRecord(right) && typeof right.text === 'string' ? right.text : undefined;\n}\n\n/** The right answers of one item (not a group), slot by slot. */\nexport function keysOf(item: unknown): KeySlot[] {\n if (!isRecord(item)) {\n return [];\n }\n const slots: KeySlot[] = [];\n const add = (slot: string, path: string[], written: readonly string[], caseSensitive = false) => {\n const answers = [...new Set(written.map((one) => keyOf(one, caseSensitive)))].filter(\n (one) => one !== '',\n );\n if (answers.length > 0) {\n slots.push({ slot, path, answers, shown: (written[0] as string).trim() });\n }\n };\n if (item.type === 'multiple-choice') {\n const options = listOf(item.options);\n const first = options.findIndex((option) => isRecord(option) && option.isCorrect === true);\n add(\n 'options',\n ['options', String(first), 'text'],\n options.flatMap((option) =>\n isRecord(option) && option.isCorrect === true && typeof option.text === 'string'\n ? [option.text]\n : [],\n ),\n );\n } else if (item.type === 'fill-in-the-blanks') {\n inPassageOrder(item, 'blanks').forEach(({ at, value: blank }, position) => {\n if (isRecord(blank)) {\n add(\n `blanks.${position}`,\n ['blanks', String(at), 'acceptedAnswers'],\n listOf(blank.acceptedAnswers).filter((one): one is string => typeof one === 'string'),\n policyFor(blank as unknown as BlankConfig).caseSensitive === true,\n );\n }\n });\n } else if (item.type === 'gap-select') {\n inPassageOrder(item, 'gaps').forEach(({ at, value: gap }, position) => {\n const text = isRecord(gap) ? choiceText(item, gap) : undefined;\n if (text !== undefined) {\n add(`gaps.${position}`, ['gaps', String(at), 'correctChoiceId'], [text]);\n }\n });\n }\n return slots;\n}\n\n/** The answers two key slots share, as the first slot writes them. */\nexport function sharedAnswers(a: KeySlot, b: KeySlot): string[] {\n return a.answers.filter((answer) => b.answers.includes(answer));\n}\n\n/** A multiple choice's options as a set of texts, for \"the same options in another order\". */\nexport function optionSet(item: unknown): string[] | null {\n if (!isRecord(item) || item.type !== 'multiple-choice') {\n return null;\n }\n const texts = listOf(item.options).map((option) =>\n isRecord(option) ? fold(stringOf(option.text)).join(' ') : '',\n );\n return texts.length === 0 || texts.some((one) => one === '') ? null : [...texts].sort();\n}\n\n// ── Length ─────────────────────────────────────────────────────────────\n\n/**\n * How far a parallel text may stray from its exemplar's length, in words:\n * `ratio` of the exemplar's words either way, and never less than `minWords`.\n * Material — a passage, a listening script, what a recording says — is held\n * closer than an item's own text, because how much a candidate reads or hears\n * is what makes one form of a part harder than another.\n */\nexport const AI_VARIANT_LENGTH_BANDS = {\n material: { ratio: 0.1, minWords: 5 },\n item: { ratio: 0.25, minWords: 2 },\n} as const;\n\n/** A host's own ratios, overriding the bands above: `{ material: 0.15 }`. */\nexport interface LengthTolerance {\n material?: number;\n item?: number;\n}\n\n/** Inclusive bounds, in words, for a text the exemplar writes in `words` words. */\nexport function lengthBand(\n words: number,\n band: 'material' | 'item',\n tolerance: LengthTolerance = {},\n): [number, number] {\n const rule = AI_VARIANT_LENGTH_BANDS[band];\n const asked = tolerance[band];\n const ratio =\n typeof asked === 'number' && Number.isFinite(asked) && asked >= 0 ? asked : rule.ratio;\n const leeway = Math.max(words * ratio, rule.minWords);\n return [Math.max(0, Math.ceil(words - leeway - 1e-9)), Math.floor(words + leeway + 1e-9)];\n}\n\n/** Every text of an entry with its band: path, kind and bounds. */\nexport function lengthTargets(\n entry: unknown,\n tolerance?: LengthTolerance,\n): { path: string[]; band: 'material' | 'item'; words: number; bounds: [number, number] }[] {\n return textsOf(entry).map((found) => {\n const words = wordsOf(found.text).length;\n return {\n path: found.path,\n band: found.band,\n words,\n bounds: lengthBand(words, found.band, tolerance),\n };\n });\n}\n\nexport interface LengthMiss {\n path: string[];\n band: 'material' | 'item';\n words: number;\n bounds: [number, number];\n exemplarWords: number;\n}\n\n/** The texts of `candidate` whose length falls outside the band of the same text in `exemplar`. */\nexport function lengthMisses(\n exemplar: unknown,\n candidate: unknown,\n tolerance?: LengthTolerance,\n): LengthMiss[] {\n const theirs = new Map(textsOf(candidate).map((found) => [found.path.join('.'), found]));\n return lengthTargets(exemplar, tolerance).flatMap((target) => {\n const found = theirs.get(target.path.join('.'));\n if (found === undefined) {\n return [];\n }\n const words = wordsOf(found.text).length;\n return words < target.bounds[0] || words > target.bounds[1]\n ? [\n {\n path: target.path,\n band: target.band,\n words,\n bounds: target.bounds,\n exemplarWords: target.words,\n },\n ]\n : [];\n });\n}\n\n// ── Structure ──────────────────────────────────────────────────────────\n\n/** One way a candidate's structure differs from its exemplar's. */\nexport interface StructureMiss {\n path: string[];\n message: string;\n}\n\nconst count = (n: number, what: string): string => `${n} ${what}${n === 1 ? '' : 's'}`;\n\n/** A script's speakers, by name, and its turns. */\nfunction scriptShape(holder: Fields): { speakers: number; turns: number } | null {\n const turns = listOf(holder.script).filter(isRecord);\n if (turns.length === 0) {\n return null;\n }\n return {\n speakers: new Set(turns.map((turn) => stringOf(turn.speaker).trim().toLowerCase())).size,\n turns: turns.length,\n };\n}\n\n/** The script a stimulus carries, on itself or on its media. */\nfunction stimulusScript(stimulus: Fields): { speakers: number; turns: number } | null {\n return scriptShape(stimulus) ?? (isRecord(stimulus.media) ? scriptShape(stimulus.media) : null);\n}\n\n/** How many turns a script of `turns` turns may have in a variant: within a quarter, at least one either way. */\nexport function turnBand(turns: number): [number, number] {\n const leeway = Math.max(turns * 0.25, 1);\n return [Math.max(1, Math.ceil(turns - leeway - 1e-9)), Math.floor(turns + leeway + 1e-9)];\n}\n\nconst mediaKind = (media: unknown): string | null =>\n isRecord(media) && typeof media.type === 'string' ? media.type : null;\n\nfunction isOneWord(answer: string): boolean {\n const trimmed = answer.trim();\n return trimmed !== '' && !/\\s/.test(trimmed);\n}\n\n/**\n * Which bank each gap draws on, by the bank's position, or `null` for a gap\n * with its own choices: in passage order.\n */\nfunction gapBanks(item: Fields): (number | null)[] {\n const banks = listOf(item.banks);\n return inPassageOrder(item, 'gaps').map(({ value: gap }) => {\n if (!isRecord(gap) || listOf(gap.choices).length > 0) {\n return null;\n }\n const at = banks.findIndex((bank) => isRecord(bank) && bank.id === gap.bankId);\n return at === -1 ? null : at;\n });\n}\n\n/**\n * The structure a variant must keep, as plain facts a model can be told: the\n * counts it must hit. Read from the exemplar; nothing here is checked.\n */\nexport function structureTargets(entry: unknown): Record<string, unknown> {\n if (!isRecord(entry)) {\n return {};\n }\n switch (entry.type) {\n case 'item-group': {\n const stimulus = isRecord(entry.stimulus) ? entry.stimulus : {};\n const script = stimulusScript(stimulus);\n return {\n stimulus: stringOf(stimulus.kind),\n items: listOf(entry.items).map((item) => ({\n type: isRecord(item) ? stringOf(item.type) : '',\n ...structureTargets(item),\n })),\n ...(script !== null ? { speakers: script.speakers, turns: turnBand(script.turns) } : {}),\n };\n }\n case 'multiple-choice': {\n const options = listOf(entry.options);\n const media = options.map((option) => (isRecord(option) ? mediaKind(option.media) : null));\n return {\n mode: stringOf(entry.mode),\n options: options.length,\n correct: options.filter((option) => isRecord(option) && option.isCorrect === true).length,\n ...(media.some((one) => one !== null) ? { optionMedia: media } : {}),\n };\n }\n case 'fill-in-the-blanks':\n return {\n blanks: inPassageOrder(entry, 'blanks').map(({ value: blank }) => {\n const answers = isRecord(blank)\n ? listOf(blank.acceptedAnswers).filter((one): one is string => typeof one === 'string')\n : [];\n return {\n oneWord: answers.length > 0 && answers.every(isOneWord),\n acceptedAnswers: [Math.max(1, answers.length - 2), answers.length + 2],\n };\n }),\n };\n case 'gap-select': {\n const banks = listOf(entry.banks);\n const drawn = gapBanks(entry);\n return {\n gaps: inPassageOrder(entry, 'gaps').map(({ value: gap }, index) =>\n drawn[index] !== null\n ? { bank: drawn[index] }\n : { choices: isRecord(gap) ? listOf(gap.choices).length : 0 },\n ),\n ...(banks.length > 0\n ? {\n banks: banks.map((bank) => ({\n choices: isRecord(bank) ? listOf(bank.choices).length : 0,\n })),\n }\n : {}),\n };\n }\n case 'written-response':\n return {\n minWords: entry.minWords,\n maxWords: entry.maxWords,\n ...(isRecord(entry.rubric)\n ? {\n criteria: listOf(entry.rubric.criteria).map((criterion) =>\n isRecord(criterion) ? stringOf(criterion.name) : '',\n ),\n }\n : {}),\n };\n default:\n return {};\n }\n}\n\n/** The settings two items must share to be scored alike, per type. */\nconst SCORED_ALIKE: Readonly<Record<string, readonly string[]>> = {\n 'multiple-choice': ['mode', 'scoringStrategy'],\n 'fill-in-the-blanks': ['scoringStrategy'],\n 'gap-select': ['scoringStrategy', 'presentation'],\n dictation: ['tolerance', 'hints'],\n 'read-aloud': ['recording', 'scoring', 'locale'],\n 'written-response': ['rubric', 'minWords', 'maxWords', 'languageTarget'],\n};\n\nconst DIFFERS: Readonly<Record<string, string>> = {\n mode: 'The mode differs',\n scoringStrategy: 'The scoring strategy differs',\n presentation: 'The presentation differs',\n tolerance: 'The tolerance differs',\n hints: 'The hints differ',\n recording: 'The recording bounds differ',\n scoring: 'The scored dimensions differ',\n locale: 'The locale differs',\n rubric: 'The rubric differs',\n minWords: 'The minimum word count differs',\n maxWords: 'The maximum word count differs',\n languageTarget: 'The language target differs',\n};\n\nfunction itemMisses(exemplar: Fields, candidate: Fields): StructureMiss[] {\n const misses: StructureMiss[] = [];\n const miss = (path: (string | number)[], message: string) =>\n misses.push({ path: path.map(String), message });\n const type = stringOf(exemplar.type);\n\n for (const field of SCORED_ALIKE[type] ?? []) {\n if (!same(exemplar[field], candidate[field])) {\n miss([field], `${DIFFERS[field]} from the exemplar's.`);\n }\n }\n if ((exemplar.shuffle === true) !== (candidate.shuffle === true)) {\n miss(['shuffle'], 'Shuffling differs from the exemplar.');\n }\n if (mediaKind(exemplar.media) !== mediaKind(candidate.media)) {\n miss(\n ['media'],\n `The exemplar has ${mediaKind(exemplar.media) ?? 'no'} media; this has ${mediaKind(candidate.media) ?? 'none'}.`,\n );\n }\n if (type === 'dictation' || type === 'read-aloud') {\n if (isRecord(exemplar.slowMedia) !== isRecord(candidate.slowMedia)) {\n miss(\n ['slowMedia'],\n isRecord(exemplar.slowMedia)\n ? 'The exemplar has a slow recording; this has none.'\n : 'This has a slow recording; the exemplar has none.',\n );\n }\n }\n\n if (type === 'multiple-choice') {\n const mine = listOf(exemplar.options);\n const theirs = listOf(candidate.options);\n if (mine.length !== theirs.length) {\n miss(['options'], `${count(theirs.length, 'option')}; the exemplar has ${mine.length}.`);\n }\n const right = (list: readonly unknown[]) =>\n list.filter((option) => isRecord(option) && option.isCorrect === true).length;\n if (right(mine) !== right(theirs)) {\n miss(\n ['options'],\n `${count(right(theirs), 'correct option')}; the exemplar has ${right(mine)}.`,\n );\n }\n const media = (list: readonly unknown[]) =>\n list\n .map((option) => (isRecord(option) ? mediaKind(option.media) : null))\n .filter((one) => one !== null)\n .sort()\n .join(',');\n if (media(mine) !== media(theirs)) {\n miss(['options'], \"The options with media differ from the exemplar's.\");\n }\n } else if (type === 'fill-in-the-blanks') {\n const mine = inPassageOrder(exemplar, 'blanks');\n const theirs = inPassageOrder(candidate, 'blanks');\n if (mine.length !== theirs.length) {\n miss(['blanks'], `${count(theirs.length, 'blank')}; the exemplar has ${mine.length}.`);\n }\n mine.forEach(({ value: blank }, index) => {\n const at = theirs[index]?.at;\n const other = theirs[index]?.value;\n if (!isRecord(blank) || !isRecord(other)) {\n return;\n }\n const policy = (one: Fields) => ({\n match: one.match,\n caseSensitive: one.caseSensitive,\n trimWhitespace: one.trimWhitespace,\n });\n if (!same(policy(blank), policy(other))) {\n miss(\n ['blanks', at as number],\n `Blank ${index + 1} matches answers differently from the exemplar's.`,\n );\n }\n const answers = (one: Fields) =>\n listOf(one.acceptedAnswers).filter(\n (answer): answer is string => typeof answer === 'string',\n );\n const exemplarAnswers = answers(blank);\n const candidateAnswers = answers(other);\n if (\n exemplarAnswers.length > 0 &&\n exemplarAnswers.every(isOneWord) &&\n !candidateAnswers.every(isOneWord)\n ) {\n miss(\n ['blanks', at as number, 'acceptedAnswers'],\n `Blank ${index + 1} takes one word in the exemplar; every answer here must be one word.`,\n );\n }\n if (Math.abs(candidateAnswers.length - exemplarAnswers.length) > 2) {\n miss(\n ['blanks', at as number, 'acceptedAnswers'],\n `Blank ${index + 1} accepts ${count(candidateAnswers.length, 'answer')}; the exemplar accepts ${exemplarAnswers.length}, and two more or fewer is the most.`,\n );\n }\n });\n } else if (type === 'gap-select') {\n const mine = inPassageOrder(exemplar, 'gaps');\n const theirs = inPassageOrder(candidate, 'gaps');\n if (mine.length !== theirs.length) {\n miss(['gaps'], `${count(theirs.length, 'gap')}; the exemplar has ${mine.length}.`);\n }\n const myBanks = listOf(exemplar.banks);\n const theirBanks = listOf(candidate.banks);\n if (myBanks.length !== theirBanks.length) {\n miss(['banks'], `${count(theirBanks.length, 'bank')}; the exemplar has ${myBanks.length}.`);\n }\n myBanks.forEach((bank, index) => {\n const other = theirBanks[index];\n const size = (one: unknown) => (isRecord(one) ? listOf(one.choices).length : 0);\n if (other !== undefined && size(bank) !== size(other)) {\n miss(\n ['banks', index, 'choices'],\n `Bank ${index + 1} offers ${count(size(other), 'choice')}; the exemplar's offers ${size(bank)}.`,\n );\n }\n });\n const myDraws = gapBanks(exemplar);\n const theirDraws = gapBanks(candidate);\n mine.forEach(({ value: gap }, index) => {\n const at = theirs[index]?.at;\n const other = theirs[index]?.value;\n if (at === undefined) {\n return;\n }\n if (myDraws[index] !== theirDraws[index]) {\n miss(\n ['gaps', at],\n `Gap ${index + 1} draws on its choices differently from the exemplar's.`,\n );\n } else if (myDraws[index] === null) {\n const size = (one: unknown) => (isRecord(one) ? listOf(one.choices).length : 0);\n if (size(gap) !== size(other)) {\n miss(\n ['gaps', at, 'choices'],\n `Gap ${index + 1} offers ${count(size(other), 'choice')}; the exemplar's offers ${size(gap)}.`,\n );\n }\n }\n });\n if ((exemplar.shuffleChoices === true) !== (candidate.shuffleChoices === true)) {\n miss(['shuffleChoices'], 'Shuffling the choices differs from the exemplar.');\n }\n }\n return misses;\n}\n\n/**\n * How `candidate`'s structure differs from `exemplar`'s: what makes the two\n * no parallel items — the type, the counts a learner answers (options and how\n * many are right, blanks, gaps, banks, a part's items and speakers), and the\n * settings that score them. Empty when they are parallel. Lengths, copies and\n * keys are not structure; see {@link lengthMisses}, {@link textSimilarity} and\n * {@link keysOf}.\n */\nexport function structureMisses(exemplar: unknown, candidate: unknown): StructureMiss[] {\n if (!isRecord(exemplar) || !isRecord(candidate)) {\n return [];\n }\n if (exemplar.type !== candidate.type) {\n return [\n {\n path: ['type'],\n message: `This is ${stringOf(candidate.type) || 'no type'}; the exemplar is ${stringOf(exemplar.type)}.`,\n },\n ];\n }\n if (exemplar.type !== 'item-group') {\n return itemMisses(exemplar, candidate);\n }\n const misses: StructureMiss[] = [];\n const mine = isRecord(exemplar.stimulus) ? exemplar.stimulus : {};\n const theirs = isRecord(candidate.stimulus) ? candidate.stimulus : {};\n if (mine.kind !== theirs.kind) {\n misses.push({\n path: ['stimulus', 'kind'],\n message: `The material is ${stringOf(theirs.kind) || 'of no kind'}; the exemplar's is ${stringOf(mine.kind)}.`,\n });\n }\n const myScript = stimulusScript(mine);\n const theirScript = stimulusScript(theirs);\n if (myScript !== null) {\n if (theirScript === null) {\n misses.push({\n path: ['stimulus', 'script'],\n message: \"The exemplar's recording has a script; this has none.\",\n });\n } else {\n if (theirScript.speakers !== myScript.speakers) {\n misses.push({\n path: ['stimulus', 'script'],\n message: `${count(theirScript.speakers, 'speaker')}; the exemplar has ${myScript.speakers}.`,\n });\n }\n const [low, high] = turnBand(myScript.turns);\n if (theirScript.turns < low || theirScript.turns > high) {\n misses.push({\n path: ['stimulus', 'script'],\n message: `${count(theirScript.turns, 'turn')}; the exemplar has ${myScript.turns}, so ${low} to ${high}.`,\n });\n }\n }\n }\n if ((exemplar.shuffle ?? 'none') !== (candidate.shuffle ?? 'none')) {\n misses.push({ path: ['shuffle'], message: 'Shuffling differs from the exemplar.' });\n }\n const myItems = listOf(exemplar.items);\n const theirItems = listOf(candidate.items);\n if (myItems.length !== theirItems.length) {\n misses.push({\n path: ['items'],\n message: `${count(theirItems.length, 'item')}; the exemplar has ${myItems.length}.`,\n });\n }\n myItems.forEach((item, index) => {\n const other = theirItems[index];\n if (other !== undefined) {\n for (const found of structureMisses(item, other)) {\n misses.push({ path: ['items', String(index), ...found.path], message: found.message });\n }\n }\n });\n return misses;\n}\n","import { containsWords, fold, passageWithoutPlaceholders } from '../answer-leak.js';\nimport type { ItemFinding } from '../types/authoring.js';\nimport {\n AI_VARIANT_NEAR_COPY_THRESHOLD,\n keysOf,\n shownScore,\n textSimilarity,\n textsOf,\n wordsOf,\n} from '../variant-rules.js';\nimport { finding } from './findings.js';\nimport { type DraftFields, isRecord } from './issues.js';\n\n/**\n * The paper critic: what only a whole paper shows, over every item of a set —\n * loose questions and the items of groups alike. Each finding sits at one of\n * the items involved and names the others in `related`.\n */\n\nconst listOf = (value: unknown): readonly unknown[] => (Array.isArray(value) ? value : []);\nconst stringOf = (value: unknown): string => (typeof value === 'string' ? value : '');\n\ninterface PaperItem {\n path: string[];\n item: DraftFields;\n /** The group it sits in: its path, so its own material is not searched for its answers. */\n group?: string[];\n}\n\ninterface Place {\n path: string[];\n /** The item it belongs to, or the group whose material it is. */\n owner: string;\n words: string[];\n}\n\nconst key = (path: readonly string[]): string => path.join('.');\n\nfunction itemsOf(entries: readonly unknown[]): PaperItem[] {\n return entries.flatMap((entry, index): PaperItem[] => {\n if (!isRecord(entry)) {\n return [];\n }\n if (entry.type === 'item-group') {\n const group = [String(index)];\n return listOf(entry.items).flatMap((item, at) =>\n isRecord(item) ? [{ path: [...group, 'items', String(at)], item, group }] : [],\n );\n }\n return typeof entry.type === 'string' ? [{ path: [String(index)], item: entry }] : [];\n });\n}\n\n/** The single-answer multiple choices a learner can see the key position of: unshuffled. */\nfunction singleAnswer(item: DraftFields): { key: number; options: readonly unknown[] } | null {\n if (item.type !== 'multiple-choice' || item.mode !== 'single') {\n return null;\n }\n const options = listOf(item.options);\n const keys = options.flatMap((option, index) =>\n isRecord(option) && option.isCorrect === true ? [index] : [],\n );\n return keys.length === 1 ? { key: keys[0] as number, options } : null;\n}\n\n// ── Key positions ──────────────────────────────────────────────────────\n\n/** Questions a position needs before \"more than half\" reads as a habit, not chance. */\nconst POSITION_BIAS_MINIMUM = 6;\n/** Keys a set needs before \"never here\" reads as a habit. */\nconst POSITION_NEVER_MINIMUM = 8;\n\nconst letter = (position: number): string =>\n position < 26 ? String.fromCharCode(65 + position) : String(position + 1);\n\nfunction keyPositionBias(items: readonly PaperItem[]): ItemFinding[] {\n const placed = items.flatMap((one) => {\n if (one.item.shuffle === true) {\n return [];\n }\n const single = singleAnswer(one.item);\n return single === null ? [] : [{ one, key: single.key, size: single.options.length }];\n });\n if (placed.length < POSITION_BIAS_MINIMUM || placed.every((at) => at.key === placed[0]?.key)) {\n // Every key in one place is `set_key_position_same`'s to say.\n return [];\n }\n const counts = new Map<number, PaperItem[]>();\n for (const at of placed) {\n counts.set(at.key, [...(counts.get(at.key) ?? []), at.one]);\n }\n for (const [position, holders] of counts) {\n if (holders.length * 2 > placed.length) {\n return [\n {\n ...finding(\n 'set_key_position_bias',\n [],\n `${holders.length} of ${placed.length} single-answer questions put the right option at ${letter(position)}. Spread the keys across positions, or shuffle the options.`,\n ),\n related: holders.map((one) => one.path),\n },\n ];\n }\n }\n if (placed.length >= POSITION_NEVER_MINIMUM) {\n const everywhere = Math.min(...placed.map((at) => at.size));\n for (let position = 0; position < everywhere; position += 1) {\n if (!counts.has(position)) {\n return [\n {\n ...finding(\n 'set_key_position_bias',\n [],\n `None of ${placed.length} single-answer questions puts the right option at ${letter(position)}, which every one of them has. A learner can rule it out.`,\n ),\n related: placed.map((at) => at.one.path),\n },\n ];\n }\n }\n }\n return [];\n}\n\n// ── The key is the longest ─────────────────────────────────────────────\n\nconst LONGEST_MINIMUM = 5;\nconst LONGEST_SHARE = 0.6;\n\nfunction keyLongestBias(items: readonly PaperItem[]): ItemFinding[] {\n const judged = items.flatMap((one) => {\n const single = singleAnswer(one.item);\n if (\n single === null ||\n single.options.length < 3 ||\n !single.options.every(\n (option) =>\n isRecord(option) &&\n option.media === undefined &&\n typeof option.text === 'string' &&\n option.text.trim() !== '',\n )\n ) {\n return [];\n }\n const lengths = single.options.map(\n (option) => [...stringOf((option as DraftFields).text).trim()].length,\n );\n const keyLength = lengths[single.key] as number;\n const longest = lengths.every((length, index) => index === single.key || length < keyLength);\n return [{ one, longest }];\n });\n const longest = judged.filter((at) => at.longest);\n if (judged.length < LONGEST_MINIMUM || longest.length < judged.length * LONGEST_SHARE) {\n return [];\n }\n return [\n {\n ...finding(\n 'set_key_longest_bias',\n [],\n `In ${longest.length} of ${judged.length} single-answer questions the right option is the longest. A learner who picks the longest option scores well without knowing the answers.`,\n ),\n related: longest.map((at) => at.one.path),\n },\n ];\n}\n\n// ── Stems that repeat ──────────────────────────────────────────────────\n\n/** Stems shorter than this are not compared: \"Choose the correct answer.\" repeats by design. */\nconst STEM_MINIMUM_WORDS = 6;\n\nfunction stemDuplicates(items: readonly PaperItem[]): ItemFinding[] {\n const stems = items.flatMap((one) =>\n textsOf(one.item)\n .filter((text) => text.kind !== 'material' && wordsOf(text.text).length >= STEM_MINIMUM_WORDS)\n .map((text) => ({ one, path: [...one.path, ...text.path], text: text.text })),\n );\n const findings: ItemFinding[] = [];\n stems.forEach((later, at) => {\n let best: { path: string[]; score: number } | undefined;\n for (const earlier of stems.slice(0, at)) {\n if (earlier.one === later.one) {\n continue;\n }\n const score = textSimilarity(earlier.text, later.text);\n if (score >= AI_VARIANT_NEAR_COPY_THRESHOLD && (best === undefined || score > best.score)) {\n best = { path: earlier.path, score };\n }\n }\n if (best !== undefined) {\n findings.push({\n ...finding(\n 'set_stem_duplicate',\n later.path,\n best.score === 1\n ? 'This reads the same as another item of the set. One piece of knowledge answers both.'\n : `This reads like another item of the set (similarity ${shownScore(best.score)}). One piece of knowledge may answer both.`,\n ),\n related: [best.path],\n });\n }\n });\n return findings;\n}\n\n// ── An answer printed in another item ──────────────────────────────────\n\n/**\n * The 100 most common English words (Oxford English Corpus), with the forms\n * of be, have and do: a key that is one of them is everywhere, so finding it\n * in another item says nothing.\n */\nconst COMMON_WORDS: ReadonlySet<string> = new Set(\n `the be to of and a in that have i it for not on with he as you do at this but his by from they we say her she or an will my one all would there their what so up out if about who get which go me when make can like time no just him know take people into year your good some could them see other than then now look only come its over think also back after use two how our work first well way even new want because any these give day most us is are was were been being am has had having does did done`.split(\n ' ',\n ),\n);\n\n/** Whether an answer is worth looking for in other items: more than two characters, and not a common word alone. */\nfunction distinctive(words: readonly string[]): boolean {\n return (\n words.join(' ').length > 2 && !(words.length === 1 && COMMON_WORDS.has(words[0] as string))\n );\n}\n\n/** What a learner reads or hears of an item: its own text, and its options' and choices'. */\nfunction placeOf(one: PaperItem): Place {\n const item = one.item;\n const texts = [\n ...textsOf(item).map((text) => text.text),\n ...listOf(item.options).map((option) => (isRecord(option) ? stringOf(option.text) : '')),\n ...listOf(item.gaps).flatMap((gap) =>\n isRecord(gap)\n ? listOf(gap.choices).map((choice) => (isRecord(choice) ? stringOf(choice.text) : ''))\n : [],\n ),\n ...listOf(item.banks).flatMap((bank) =>\n isRecord(bank)\n ? listOf(bank.choices).map((choice) => (isRecord(choice) ? stringOf(choice.text) : ''))\n : [],\n ),\n ];\n return {\n path: one.path,\n owner: key(one.path),\n words: fold(passageWithoutPlaceholders(texts.join('\\n'))),\n };\n}\n\n/** An item's own words, where its answer must not already be: its stem, not its options. */\nfunction ownWords(item: DraftFields): string[] {\n return fold(\n passageWithoutPlaceholders(\n textsOf(item)\n .filter((text) => text.kind !== 'material')\n .map((text) => text.text)\n .join('\\n'),\n ),\n );\n}\n\nfunction answersInOtherItems(\n entries: readonly unknown[],\n items: readonly PaperItem[],\n): ItemFinding[] {\n const places: Place[] = [\n ...items.map(placeOf),\n ...entries.flatMap((entry, index): Place[] => {\n if (!isRecord(entry) || entry.type !== 'item-group') {\n return [];\n }\n const material = textsOf({ type: 'item-group', stimulus: entry.stimulus })\n .map((text) => text.text)\n .join('\\n');\n return material === ''\n ? []\n : [{ path: [String(index), 'stimulus'], owner: `group:${index}`, words: fold(material) }];\n }),\n ];\n const findings: ItemFinding[] = [];\n for (const one of items) {\n const own = ownWords(one.item);\n const ownGroup = one.group !== undefined ? `group:${one.group[0]}` : undefined;\n const reported = new Set<string>();\n for (const slot of keysOf(one.item)) {\n for (const answer of slot.answers) {\n const words = fold(answer);\n if (!distinctive(words) || containsWords(own, words)) {\n continue;\n }\n for (const place of places) {\n if (\n place.owner === key(one.path) ||\n place.owner === ownGroup ||\n reported.has(place.owner)\n ) {\n continue;\n }\n if (containsWords(place.words, words)) {\n reported.add(place.owner);\n findings.push({\n ...finding(\n 'set_item_answers_item',\n [...one.path, ...slot.path],\n `The answer \"${answer}\" is printed elsewhere in the set: a learner can find it there.`,\n ),\n related: [place.path],\n });\n }\n }\n }\n }\n }\n return findings;\n}\n\n// ── One answer, many items ─────────────────────────────────────────────\n\nconst REPEATED_KEY_MINIMUM = 3;\n\nfunction repeatedKeys(items: readonly PaperItem[]): ItemFinding[] {\n const holders = new Map<string, string[][]>();\n for (const one of items) {\n const seen = new Set<string>();\n for (const slot of keysOf(one.item)) {\n for (const answer of slot.answers) {\n if (!seen.has(answer)) {\n seen.add(answer);\n holders.set(answer, [...(holders.get(answer) ?? []), [...one.path, ...slot.path]]);\n }\n }\n }\n }\n return [...holders].flatMap(([answer, paths]) =>\n paths.length >= REPEATED_KEY_MINIMUM\n ? [\n {\n ...finding(\n 'set_key_repeated',\n paths[0] as string[],\n `\"${answer}\" is the answer to ${paths.length} items. The set tests less than its length suggests.`,\n ),\n related: paths.slice(1),\n },\n ]\n : [],\n );\n}\n\n/**\n * What only a whole paper shows, over every item of `entries` — loose\n * questions and the items of groups: keys clustered in one position, keys\n * that are the longest option, stems that repeat, an answer printed in another\n * item, and one answer to many items. Paths are from the set's root.\n */\nexport function paperFindings(entries: readonly unknown[]): ItemFinding[] {\n const items = itemsOf(entries);\n return [\n ...keyPositionBias(items),\n ...keyLongestBias(items),\n ...stemDuplicates(items),\n ...answersInOtherItems(entries, items),\n ...repeatedKeys(items),\n ];\n}\n","import { UnknownActivityTypeError } from '../errors.js';\nimport { getActivityTypeDescriptor } from '../registry/index.js';\nimport { GROUP_CAPTIONS_REVEAL_DICTATION, groupCaptionsField } from '../schemas/dictation.js';\nimport { ItemGroupSchema, validateItemGroup } from '../schemas/item-group.js';\nimport { INTERACTIVE_VIDEO_ITEM_TYPES, isInteractiveVideoItemType } from '../timeline-limits.js';\nimport type { ActivityType } from '../types/activity.js';\nimport type {\n DraftContext,\n DraftIssue,\n DraftValidationResult,\n ItemFinding,\n} from '../types/authoring.js';\nimport type { ItemGroup } from '../types/item-group.js';\nimport { withValidationScope } from '../validation-scope.js';\nimport { keyPositionFinding } from './critique.js';\nimport { critiqueDraft, validateDraft } from './index.js';\nimport {\n checkMedia,\n covers,\n type DraftFields,\n isMissingId,\n isRecord,\n issue,\n isUnset,\n isUnwritten,\n refusesEmpty,\n} from './issues.js';\nimport { paperFindings } from './paper.js';\nimport { stimulusFindings } from './recordings.js';\n\n/**\n * A new, empty reading or listening testlet for an editor to start from.\n *\n * The stimulus starts as `text`, the only kind that needs no media file to be\n * a valid draft — an author who is writing a passage can begin typing, and one\n * who is building a listening item changes `kind` before uploading. `items`\n * starts empty, exactly as a new fill-in-the-blanks draft starts with no\n * blanks: the group is `incomplete` until somebody writes a question into it.\n *\n * @throws Error when `newId` returns an empty or repeated id.\n */\nexport function createItemGroupDraft(context: DraftContext): ItemGroup {\n const issued = new Set<string>();\n const newId = (): string => {\n const id: unknown = context.newId();\n if (typeof id !== 'string' || id === '') {\n throw new Error('createItemGroupDraft: newId() must return a non-empty string.');\n }\n if (issued.has(id)) {\n throw new Error(\n `createItemGroupDraft: newId() returned \"${id}\" twice. Ids within one draft must differ.`,\n );\n }\n issued.add(id);\n return id;\n };\n return {\n schemaVersion: '1.0',\n type: 'item-group',\n id: newId(),\n stimulus: { id: newId(), kind: 'text', body: '' },\n items: [],\n };\n}\n\n/**\n * A new, empty interactive video for an editor to start from: an item group\n * whose stimulus is a video with no file yet, no questions, and a timeline with\n * no quizzes.\n *\n * It is an ordinary item group — {@link validateItemGroupDraft} checks it, and\n * everything that reads groups reads it — because that is what keeps each\n * question its own slot. Like every new draft it is `incomplete`: an untouched\n * draft must never read as finished.\n *\n * @throws Error when `newId` returns an empty or repeated id.\n */\nexport function createInteractiveVideoDraft(context: DraftContext): ItemGroup {\n const draft = createItemGroupDraft(context);\n return {\n ...draft,\n stimulus: { id: draft.stimulus.id, kind: 'video', media: { type: 'video', url: '' } },\n timeline: { cues: [] },\n };\n}\n\n/**\n * Tells an unfinished testlet from a broken one — {@link validateDraft} for the\n * container that holds several questions around one passage or recording.\n *\n * It is a **separate entry point rather than a `'item-group'` type**, because\n * `validateDraft('item-group', …)` throws by design: `item-group` is reserved\n * in the registry precisely so it can never be registered as an activity, and\n * that guard is what keeps `isItemGroup` and `flattenSequence` able to trust\n * their own container. Widening `validateDraft` to accept it would have meant\n * weakening that.\n *\n * Each item is checked by `validateDraft` for its own type and its issues are\n * re-pathed under `items.N.…`, so an editor gets one list for the whole group\n * and every code in it is the code that type already documents. An item whose\n * type nobody registered is REPORTED, never thrown — an author fixing a\n * six-item group wants all six problems, not the first one that blew up.\n *\n * The group is `complete` only when the container passes, every item passes its\n * own schema, and every item is itself `complete`.\n *\n * @throws the error a registered item type's `checkDraft` or schema throws, as\n * `validateDraft` does for that item on its own. Only an unregistered type is\n * turned into an issue: a fault in a type's own code is not a problem with the\n * draft, and reporting it as an unknown type would hide it.\n */\n/**\n * The item critic over a testlet: each item's findings, under `items.N.…`, and\n * what only the set shows — every single-answer question keeping its right\n * option in one place. The stimulus is not searched for answers: a reading\n * group's answers are meant to be in its passage.\n *\n * An item of a type that is not registered, or without a `type`, is skipped:\n * {@link validateItemGroupDraft} already reports it.\n */\nexport function critiqueItemGroupDraft(draft: unknown): ItemFinding[] {\n if (!isRecord(draft)) {\n return [];\n }\n const items = Array.isArray(draft.items) ? draft.items : [];\n return withValidationScope(() => [\n ...stimulusFindings(draft.stimulus),\n ...items.flatMap((item, index) => repathed(critiqueItem(item), ['items', index])),\n ...keyPositionFinding(items, ['items']),\n ]);\n}\n\n/**\n * The item critic over a question set, as `<ActivitySequence>` takes one: each\n * entry's findings under its index — an item group's under `N.items.M.…` — and,\n * over the entries that are items, what only the set shows (see\n * {@link critiqueItemGroupDraft}). An entry of a type that is not registered is\n * skipped.\n */\nexport function critiqueDrafts(entries: readonly unknown[]): ItemFinding[] {\n return withValidationScope(() => [\n ...entries.flatMap((entry, index) =>\n repathed(\n isRecord(entry) && entry.type === 'item-group'\n ? critiqueItemGroupDraft(entry)\n : critiqueItem(entry),\n [index],\n ),\n ),\n ...keyPositionFinding(entries, []),\n ...paperFindings(entries),\n ]);\n}\n\n/** One item's findings, or none for an item the critic cannot place. */\nfunction critiqueItem(item: unknown): ItemFinding[] {\n if (!isRecord(item) || typeof item.type !== 'string') {\n return [];\n }\n if (getActivityTypeDescriptor(item.type) === undefined) {\n return [];\n }\n return critiqueDraft(item.type as ActivityType, item);\n}\n\nconst repathed = (findings: ItemFinding[], prefix: (string | number)[]): ItemFinding[] =>\n findings.map((found) => ({ ...found, path: [...prefix.map(String), ...found.path] }));\n\nexport function validateItemGroupDraft(draft: unknown): DraftValidationResult<ItemGroup> {\n // Every item is checked as a draft and then again as part of the group: one\n // scope lets the expensive normalisation behind both run once.\n return withValidationScope(() => validateItemGroupDraftInScope(draft));\n}\n\nfunction validateItemGroupDraftInScope(draft: unknown): DraftValidationResult<ItemGroup> {\n const issues: DraftIssue[] = [];\n if (!isRecord(draft)) {\n issues.push(\n issue('ig_not_an_object', [], 'A group must be an object with a stimulus and its items.'),\n );\n return { status: 'invalid', issues };\n }\n\n issues.push(...checkGroupIdentity(draft));\n if (isRecord(draft.stimulus)) {\n issues.push(...checkStimulus(draft.stimulus));\n } else if (isUnset(draft.stimulus)) {\n issues.push(\n issue(\n 'ig_stimulus_required',\n ['stimulus'],\n 'Add the passage, recording or image the questions are about.',\n ),\n );\n }\n issues.push(...checkItems(draft));\n if (draft.timeline !== undefined && draft.timeline !== null) {\n issues.push(...checkTimeline(draft));\n }\n const captions = groupCaptionsField(draft, (captionsUrl) => !isUnwritten(captionsUrl));\n if (captions !== undefined) {\n issues.push(\n issue(\n 'dc_captions_not_allowed',\n ['stimulus', 'media', captions],\n GROUP_CAPTIONS_REVEAL_DICTATION,\n ),\n );\n }\n\n // Every container failure the checks above did not already account for, added\n // as `invalid` — the same rule `validateDraft` follows, so a rule nobody\n // named still reaches the author instead of passing silently.\n const parsed = ItemGroupSchema.safeParse(draft, { reportInput: true });\n if (!parsed.success) {\n for (const schemaIssue of parsed.error.issues) {\n const path = schemaIssue.path.map(String);\n if (issues.some((known) => covers(known.path, path))) {\n continue;\n }\n if (path.length > 0 && refusesEmpty(draft, schemaIssue)) {\n issues.push(\n issue(\n 'null_not_allowed',\n path,\n 'This field cannot be null. Leave it out if it is optional, or give it a value.',\n ),\n );\n continue;\n }\n issues.push({\n path,\n message: schemaIssue.message,\n code: schemaIssue.code,\n severity: 'invalid',\n });\n }\n }\n\n const stored = validateItemGroup(draft);\n if (issues.length === 0 && stored.success) {\n return { status: 'complete', data: stored.data, issues };\n }\n if (issues.length === 0) {\n // The container and every item satisfy their own checks, yet the group is\n // not storable. Nothing here can name the cause, so hand over the\n // validator's own errors rather than claim the draft is finished.\n for (const error of stored.success ? [] : stored.errors) {\n issues.push({ ...error, severity: 'invalid' });\n }\n }\n const invalid = issues.some((found) => found.severity !== 'incomplete');\n return { status: invalid ? 'invalid' : 'incomplete', issues };\n}\n\n/**\n * An interactive video: every question in exactly one quiz, quizzes at real\n * moments, a video to hang them on, and only the five question types a video\n * may hold. Each check stands aside where another already names the problem —\n * an unset item type is `ig_item_type_required`, an unregistered one\n * `ig_item_type_unknown` — so an author sees one issue per mistake.\n */\nfunction checkTimeline(draft: DraftFields): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const timeline = draft.timeline;\n if (!isRecord(timeline)) {\n return issues;\n }\n const stimulus = isRecord(draft.stimulus) ? draft.stimulus : undefined;\n if (stimulus !== undefined && !isUnwritten(stimulus.kind) && stimulus.kind !== 'video') {\n issues.push(\n issue(\n 'ig_timeline_stimulus_kind',\n ['stimulus', 'kind'],\n 'An interactive video needs a video: quizzes open at moments of it.',\n ),\n );\n } else if (\n stimulus !== undefined &&\n isRecord(stimulus.media) &&\n !isUnwritten(stimulus.media.type) &&\n stimulus.media.type !== 'video'\n ) {\n issues.push(\n issue(\n 'ig_timeline_stimulus_kind',\n ['stimulus', 'media', 'type'],\n 'Upload a video file. An embedded player from another site cannot be paused when a quiz opens.',\n ),\n );\n }\n if (draft.shuffle === 'within-group') {\n issues.push(\n issue(\n 'ig_timeline_shuffle',\n ['shuffle'],\n 'The questions of an interactive video appear in the order their quizzes open, so they cannot be shuffled.',\n ),\n );\n }\n\n const items = Array.isArray(draft.items) ? draft.items : [];\n const itemIndexById = new Map<string, number>();\n items.forEach((item, index) => {\n if (!isRecord(item)) {\n return;\n }\n if (typeof item.id === 'string' && item.id !== '') {\n itemIndexById.set(item.id, index);\n }\n const type = item.type;\n // Registered but not one of the six: the unset and unregistered cases are\n // already `checkItems`'s to report.\n if (\n typeof type === 'string' &&\n type !== '' &&\n type !== 'item-group' &&\n !isInteractiveVideoItemType(type) &&\n getActivityTypeDescriptor(type) !== undefined\n ) {\n issues.push(\n issue(\n 'ig_timeline_item_type',\n ['items', index, 'type'],\n `An interactive video can hold ${INTERACTIVE_VIDEO_ITEM_TYPES.join(', ')} questions only.`,\n ),\n );\n }\n if (type === 'dictation' && isUnset(item.media)) {\n issues.push(\n issue(\n 'ig_timeline_dictation_media',\n ['items', index, 'media'],\n 'Add the recording this dictation plays: inside a video it cannot play the video.',\n ),\n );\n }\n });\n\n const cues = Array.isArray(timeline.cues) ? timeline.cues : [];\n const quizIds = new Set<string>();\n const placed = new Set<string>();\n cues.forEach((cue, cueIndex) => {\n if (!isRecord(cue)) {\n return;\n }\n const at = (field: string, ...rest: (string | number)[]): (string | number)[] => [\n 'timeline',\n 'cues',\n cueIndex,\n field,\n ...rest,\n ];\n if (isMissingId(cue.id)) {\n issues.push(issue('ig_timeline_quiz_id_required', at('id'), 'This quiz has no id.'));\n } else if (typeof cue.id === 'string') {\n if (quizIds.has(cue.id)) {\n issues.push(\n issue('ig_timeline_quiz_id_duplicate', at('id'), `Two quizzes share the id \"${cue.id}\".`),\n );\n }\n quizIds.add(cue.id);\n }\n if (isUnset(cue.at)) {\n issues.push(\n issue(\n 'ig_timeline_quiz_time_required',\n at('at'),\n 'Choose when in the video this quiz opens.',\n ),\n );\n } else if (typeof cue.at !== 'number' || !Number.isFinite(cue.at) || cue.at < 0) {\n issues.push(\n issue(\n 'ig_timeline_quiz_time_invalid',\n at('at'),\n 'A quiz opens at a number of seconds, 0 or more.',\n ),\n );\n }\n if (isUnset(cue.itemIds) || (Array.isArray(cue.itemIds) && cue.itemIds.length === 0)) {\n issues.push(\n issue('ig_timeline_quiz_empty', at('itemIds'), 'Add at least one question to this quiz.'),\n );\n return;\n }\n if (!Array.isArray(cue.itemIds)) {\n return;\n }\n cue.itemIds.forEach((itemId: unknown, position: number) => {\n if (typeof itemId !== 'string') {\n return;\n }\n if (!itemIndexById.has(itemId)) {\n issues.push(\n issue(\n 'ig_timeline_quiz_unknown_item',\n at('itemIds', position),\n `This quiz names a question that is not in the video (\"${itemId}\").`,\n ),\n );\n } else if (placed.has(itemId)) {\n issues.push(\n issue(\n 'ig_timeline_item_duplicate',\n at('itemIds', position),\n 'This question is already in a quiz: each question belongs to one quiz.',\n ),\n );\n }\n placed.add(itemId);\n });\n });\n for (const [itemId, index] of itemIndexById) {\n if (!placed.has(itemId)) {\n issues.push(\n issue(\n 'ig_timeline_item_unplaced',\n ['items', index],\n 'Add this question to a quiz, or the video never shows it.',\n ),\n );\n }\n }\n\n const chapters = Array.isArray(timeline.chapters) ? timeline.chapters : [];\n let previousAt: number | undefined;\n chapters.forEach((chapter, index) => {\n if (!isRecord(chapter)) {\n return;\n }\n if (isUnwritten(chapter.title)) {\n issues.push(\n issue(\n 'ig_timeline_chapter_title',\n ['timeline', 'chapters', index, 'title'],\n 'Give this chapter a title.',\n ),\n );\n }\n if (typeof chapter.at === 'number' && Number.isFinite(chapter.at)) {\n if (previousAt !== undefined && chapter.at <= previousAt) {\n issues.push(\n issue(\n 'ig_timeline_chapter_order',\n ['timeline', 'chapters', index, 'at'],\n 'Chapters must start in order, each after the one before it.',\n ),\n );\n }\n previousAt = chapter.at;\n }\n });\n return issues;\n}\n\n/** The group's own envelope. `title` is optional on a group, unlike an activity. */\nfunction checkGroupIdentity(draft: DraftFields): DraftIssue[] {\n const issues: DraftIssue[] = [];\n if (draft.schemaVersion !== '1.0') {\n issues.push(\n issue(\n 'schema_version_invalid',\n ['schemaVersion'],\n 'The draft must have schemaVersion \"1.0\". A draft from createItemGroupDraft has it.',\n ),\n );\n }\n if (draft.type !== 'item-group') {\n issues.push(issue('type_mismatch', ['type'], 'The draft\\'s type must be \"item-group\".'));\n }\n if (isMissingId(draft.id)) {\n issues.push(issue('id_required', ['id'], 'The group has no id.'));\n }\n if (!isUnset(draft.shuffle) && draft.shuffle !== 'none' && draft.shuffle !== 'within-group') {\n issues.push(\n issue(\n 'ig_shuffle_invalid',\n ['shuffle'],\n 'Shuffling is either \"none\" or \"within-group\". Leave it out to keep the authored order.',\n ),\n );\n }\n return issues;\n}\n\nconst KINDS = new Set(['text', 'audio', 'video', 'image', 'mixed']);\n/** Which media type each stimulus kind accepts — mirrors `StimulusSchema`'s refinement. */\nconst MEDIA_FOR_KIND: Record<string, readonly string[]> = {\n audio: ['audio'],\n video: ['video', 'embed'],\n image: ['image'],\n mixed: ['audio', 'video', 'image', 'embed'],\n};\n\n/**\n * The passage, recording or image. The split this makes is the one the whole\n * draft contract exists for: a kind nobody has chosen and a body nobody has\n * typed are `incomplete`, while a recording attached to an image stimulus is\n * `invalid` — writing more will not reconcile them.\n */\nfunction checkStimulus(stimulus: DraftFields): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const at = (...rest: (string | number)[]) => ['stimulus', ...rest];\n\n if (isMissingId(stimulus.id)) {\n issues.push(issue('ig_stimulus_id_required', at('id'), 'The stimulus has no id.'));\n }\n\n const kind = stimulus.kind;\n if (isUnwritten(kind)) {\n issues.push(\n issue(\n 'ig_stimulus_kind_required',\n at('kind'),\n 'Choose what the questions are about: a text, a recording, a video, an image, or a mix.',\n ),\n );\n } else if (typeof kind !== 'string' || !KINDS.has(kind)) {\n // A value of another type is the schema's to refuse.\n if (typeof kind === 'string') {\n issues.push(\n issue(\n 'ig_stimulus_kind_invalid',\n at('kind'),\n `\"${kind}\" is not a stimulus kind. Use text, audio, video, image or mixed.`,\n ),\n );\n }\n // Only the kind-DEPENDENT rules are skipped. The media still has problems\n // of its own — a refused address, a missing description — and returning\n // here left every one of them to the schema, so an unrecognised kind made\n // zod's raw diagnostics appear beside ours.\n if (isRecord(stimulus.media)) {\n issues.push(...checkMedia(stimulus.media, at('media')));\n }\n return issues;\n }\n\n const hasBody = typeof stimulus.body === 'string' && stimulus.body.trim() !== '';\n // One rule, one issue, at `body` — where the schema reports both halves of\n // it. Written as two branches, a `text` stimulus carrying rich text and no\n // plain text reported the same path twice; and keying the second branch on\n // whether `bodyHtml` had CONTENT missed the empty string a cleared rich-text\n // editor leaves behind, which the schema still refuses.\n const htmlPresent = !isUnset(stimulus.bodyHtml);\n if (!hasBody && (kind === 'text' || kind === 'mixed' || htmlPresent)) {\n issues.push(\n issue(\n 'ig_stimulus_body_required',\n at('body'),\n isUnwritten(stimulus.bodyHtml)\n ? 'Write the passage.'\n : 'Write the passage as plain text too. The rich-text version is shown only where a sanitiser is supplied.',\n ),\n );\n }\n\n if (kind !== 'text' && isUnset(stimulus.media)) {\n issues.push(\n issue('ig_stimulus_media_required', at('media'), 'Add the recording, video or image.'),\n );\n } else if (isRecord(stimulus.media)) {\n const allowed = MEDIA_FOR_KIND[kind as string];\n const mediaType = stimulus.media.type;\n if (allowed !== undefined && typeof mediaType === 'string' && !allowed.includes(mediaType)) {\n issues.push(\n issue(\n 'ig_stimulus_media_kind',\n at('media', 'type'),\n `A \"${kind}\" stimulus cannot carry ${mediaType} media. Use ${allowed.join(' or ')}.`,\n ),\n );\n }\n issues.push(...checkMedia(stimulus.media, at('media')));\n }\n return issues;\n}\n\n/**\n * Every item, checked by `validateDraft` for its own type.\n *\n * Re-pathing rather than re-implementing is the point: a multiple-choice\n * question inside a testlet reports exactly the codes a standalone one does,\n * so a consumer's translation table covers both and a rule can never drift\n * between them.\n */\nfunction checkItems(draft: DraftFields): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const items = isUnset(draft.items) ? [] : draft.items;\n if (!Array.isArray(items)) {\n return issues;\n }\n if (items.length === 0) {\n issues.push(\n issue('ig_items_required', ['items'], 'Add at least one question about this material.'),\n );\n return issues;\n }\n\n const seen = new Set<string>();\n const repeated = new Set<string>();\n for (const [index, item] of items.entries()) {\n if (!isRecord(item)) {\n continue;\n }\n const ordinal = index + 1;\n if (isMissingId(item.id)) {\n issues.push(\n issue('ig_item_id_required', ['items', index, 'id'], `Question ${ordinal} has no id.`),\n );\n } else if (typeof item.id === 'string') {\n if (seen.has(item.id)) {\n repeated.add(item.id);\n }\n seen.add(item.id);\n }\n\n if (item.type === 'item-group') {\n issues.push(\n issue(\n 'ig_item_nested_group',\n ['items', index, 'type'],\n 'Groups do not nest: every item must be an activity.',\n ),\n );\n continue;\n }\n if (isUnwritten(item.type)) {\n issues.push(\n issue(\n 'ig_item_type_required',\n ['items', index, 'type'],\n `Choose what kind of question ${ordinal} is.`,\n ),\n );\n continue;\n }\n if (typeof item.type !== 'string') {\n continue;\n }\n\n let result: DraftValidationResult<unknown>;\n try {\n result = validateDraft(item.type as ActivityType, item);\n } catch (error) {\n // Only an unregistered type is reported, never thrown — the same choice\n // `validateItemGroup` makes, for the same reason. Anything else is a fault\n // in a registered type's own checks or schema: it is rethrown, as\n // `validateDraft` throws it for the item on its own, because reported here\n // it read as an unknown type and sent the author after a mistake the draft\n // did not have. The error must name this item's type, too: a check that\n // validates a part of the item as some other, unregistered type has not\n // made this item's type unknown.\n if (!(error instanceof UnknownActivityTypeError) || error.activityType !== item.type) {\n throw error;\n }\n issues.push(\n issue(\n 'ig_item_type_unknown',\n ['items', index, 'type'],\n `\"${item.type}\" is not a registered activity type.`,\n ),\n );\n continue;\n }\n for (const found of result.issues) {\n issues.push({ ...found, path: ['items', String(index), ...found.path] });\n }\n }\n\n for (const id of repeated) {\n issues.push(\n issue('ig_item_id_duplicate', ['items'], `More than one question has the id \"${id}\".`),\n );\n }\n return issues;\n}\n","import { UnknownActivityTypeError } from '../errors.js';\nimport { getActivityTypeDescriptor } from '../registry/index.js';\nimport { readSchema } from '../schemas/read-schema.js';\nimport type { ActivityDataMap, ActivityType } from '../types/activity.js';\nimport type {\n DraftContext,\n DraftIssue,\n DraftValidationResult,\n ItemFinding,\n} from '../types/authoring.js';\nimport { withValidationScope } from '../validation-scope.js';\nimport { normaliseFinding } from './findings.js';\nimport {\n coveredPathsOf,\n DRAFT_ISSUE_SEVERITY,\n type DraftIssueCode,\n isRecord,\n issue,\n pathKey,\n refusesEmpty,\n} from './issues.js';\nimport { recordingFindings } from './recordings.js';\n\n/**\n * Tells a draft that is not finished from a draft that is wrong.\n *\n * `validateActivity` answers \"may this be stored?\", and to that question an\n * empty new question and a broken one are the same answer: no. An editor needs\n * the difference — \"Add a title\" is a to-do, \"Only one option can be marked\n * correct\" is an error — so a freshly added question does not read as a\n * failure, disable a whole form, or blank a preview.\n *\n * - `complete` — the draft is a valid activity with nothing left to write.\n * `data` is exactly what `validateActivity` returns for it.\n * - `incomplete` — every problem is something not yet written.\n * - `invalid` — at least one problem is wrong in a way writing more cannot fix.\n *\n * It is stricter than `validateActivity`, never looser: `complete` requires the\n * activity's schema to pass, and a draft can fail here that the schema accepts\n * (a title that is only whitespace). Content stored by an older build can\n * therefore be valid and still not `complete`. `validateActivity` stays the\n * check at your write boundary.\n *\n * Issues come from the type's `authoring.checkDraft`, plus every schema failure\n * it did not already report at that path or inside it. (A failure at the root of\n * the draft counts as reported only by an issue at the root, since every path is\n * inside the root.) Such a failure is added as `invalid`: under the code\n * `null_not_allowed` when the schema refused a `null` there, or an `undefined`\n * entry in a list, and otherwise under the schema's own code and message. A type\n * with no `checkDraft` therefore reports every schema failure as `invalid`.\n *\n * The codes documented in `docs/authoring.md` are a contract, and each is\n * reported with its documented severity, whichever check reports it. A code\n * passed through from the schema is not a contract: the code and its English\n * message belong to the schema library, so give a code you do not recognise a\n * generic message of your own.\n *\n * @throws UnknownActivityTypeError when `type` has no registered descriptor —\n * including `'item-group'`, which is a container, not an activity type.\n */\nexport function validateDraft<T extends ActivityType>(\n type: T,\n draft: unknown,\n): DraftValidationResult<ActivityDataMap[T]> {\n // The type's checks and its schema read the same strings: one scope lets the\n // expensive normalisation behind both run once.\n return withValidationScope(() => validateDraftInScope(type, draft));\n}\n\nfunction validateDraftInScope<T extends ActivityType>(\n type: T,\n draft: unknown,\n): DraftValidationResult<ActivityDataMap[T]> {\n const descriptor = getActivityTypeDescriptor(type);\n if (descriptor === undefined) {\n throw new UnknownActivityTypeError(String(type));\n }\n\n const reported =\n isRecord(draft) && descriptor.authoring?.checkDraft !== undefined\n ? descriptor.authoring.checkDraft(draft).map(normalise)\n : [];\n const issues: DraftIssue[] = [...reported];\n\n // `reportInput` records on each issue the value its check was given, which is\n // how a refused `null` is told apart from a rule that merely points at one.\n // A registered type's schema, read through Standard Schema, reports the value\n // at the issue's path instead.\n const parsed = readSchema(descriptor.schema, draft, `Activity type \"${descriptor.type}\"`, {\n reportInput: true,\n });\n if (!parsed.success) {\n const covered = coveredPathsOf(reported);\n const reportedEmpty = new Set<string>();\n for (const schemaIssue of parsed.issues) {\n const path = schemaIssue.path.map(String);\n if (covered.has(pathKey(path))) {\n continue;\n }\n // `null` is how many editors and databases spell \"no value\". Where the\n // schema refuses one, name it under one documented code, for every type,\n // rather than hand over the schema library's own diagnostic.\n if (path.length > 0 && refusesEmpty(draft, schemaIssue)) {\n const key = JSON.stringify(path);\n if (!reportedEmpty.has(key)) {\n reportedEmpty.add(key);\n issues.push(\n issue(\n 'null_not_allowed',\n path,\n typeof schemaIssue.path.at(-1) === 'number'\n ? 'This entry has no value. Remove it, or fill it in.'\n : 'This field cannot be null. Leave it out if it is optional, or give it a value.',\n ),\n );\n }\n continue;\n }\n issues.push({\n path,\n message: schemaIssue.message,\n code: schemaIssue.code,\n severity: 'invalid',\n });\n }\n }\n\n if (parsed.success && issues.length === 0) {\n return { status: 'complete', data: parsed.data as ActivityDataMap[T], issues };\n }\n const invalid = issues.some((found) => found.severity !== 'incomplete');\n return { status: invalid ? 'invalid' : 'incomplete', issues };\n}\n\n/**\n * A new, empty draft of an activity type, for an editor to start from.\n *\n * Every required field is present and nothing is authored, so the draft comes\n * back from {@link validateDraft} as `incomplete`, and `validateActivity`\n * rejects it until someone writes it. The SDK invents no ids: `newId` is called\n * once per id the draft needs, and must return a different non-empty string each\n * time.\n *\n * ```ts\n * const draft = createDraft('multiple-choice', { newId: () => crypto.randomUUID() });\n * ```\n *\n * @throws UnknownActivityTypeError when `type` has no registered descriptor.\n * @throws Error when the type declares no `authoring.createDraft`, or when\n * `newId` returns an empty or repeated id.\n */\nexport function createDraft<T extends ActivityType>(\n type: T,\n context: DraftContext,\n): ActivityDataMap[T] {\n const descriptor = getActivityTypeDescriptor(type);\n if (descriptor === undefined) {\n throw new UnknownActivityTypeError(String(type));\n }\n const create = descriptor.authoring?.createDraft;\n if (create === undefined) {\n throw new Error(\n `Activity type \"${String(type)}\" declares no authoring.createDraft, so there is no empty draft to create. ` +\n 'Build the draft yourself; validateDraft works for every registered type.',\n );\n }\n const issued = new Set<string>();\n const newId = (): string => {\n const id: unknown = context.newId();\n if (typeof id !== 'string' || id === '') {\n throw new Error('createDraft: newId() must return a non-empty string.');\n }\n if (issued.has(id)) {\n throw new Error(\n `createDraft: newId() returned \"${id}\" twice. Ids within one draft must differ — two options sharing an id cannot be scored apart.`,\n );\n }\n issued.add(id);\n return id;\n };\n return create({ newId }) as ActivityDataMap[T];\n}\n\n/**\n * A check's issue as `validateDraft` reports it.\n *\n * A documented code carries its documented severity, whichever check reports\n * it. Any other code keeps the check's severity, and one that is not\n * `'incomplete'` — misspelled, or missing — is reported as `invalid`, so the\n * mistake fails closed. Path segments become strings, as the schema's are, so a\n * list index given as a number still covers the schema's failure there.\n */\nfunction normalise(found: DraftIssue): DraftIssue {\n const documented = Object.hasOwn(DRAFT_ISSUE_SEVERITY, found.code)\n ? DRAFT_ISSUE_SEVERITY[found.code as DraftIssueCode]\n : undefined;\n return {\n ...found,\n path: found.path.map(String),\n severity: documented ?? (found.severity === 'incomplete' ? 'incomplete' : 'invalid'),\n };\n}\n\n/**\n * What an experienced item writer would point out on review: flaws a learner\n * can exploit or trip over (`warning`) and item-writing guidelines the item\n * departs from (`advice`) — the right option much longer than the rest, an\n * answer printed in the passage, a hint that gives the answer away, two\n * options that read the same, \"all of the above\" under shuffle.\n *\n * A finding is never a reason to refuse an item. It does not change what\n * {@link validateDraft} says: a valid item with findings is still valid. The\n * shape is a `DraftIssue`'s, so an editor shows both in one list.\n *\n * It reads a draft, finished or not, and skips any rule whose fields are not\n * written yet. The codes are documented in `docs/authoring.md`, each with its\n * severity; the rule behind a code is advice and may be refined in a minor.\n *\n * ```ts\n * const list = [...validateDraft(type, draft).issues, ...critiqueDraft(type, draft)];\n * ```\n *\n * @throws UnknownActivityTypeError when `type` has no registered descriptor —\n * including `'item-group'`: use {@link critiqueItemGroupDraft}.\n */\nexport function critiqueDraft(type: ActivityType, draft: unknown): ItemFinding[] {\n const descriptor = getActivityTypeDescriptor(type);\n if (descriptor === undefined) {\n throw new UnknownActivityTypeError(String(type));\n }\n if (!isRecord(draft)) {\n return [];\n }\n const critique = descriptor.authoring?.critique;\n return withValidationScope(() => [\n ...(critique === undefined ? [] : critique(draft)),\n // Every type's recordings, wherever its type keeps media.\n ...recordingFindings(draft),\n ]).map(normaliseFinding);\n}\n\nexport {\n createInteractiveVideoDraft,\n createItemGroupDraft,\n critiqueDrafts,\n critiqueItemGroupDraft,\n validateItemGroupDraft,\n} from './item-group.js';\n","import { cleanText, folded, needle, provenanceOf, usageOf } from './ai-text.js';\nimport { critiqueDraft } from './authoring/index.js';\nimport { UnknownActivityTypeError } from './errors.js';\nimport { getActivityTypeDescriptor } from './registry/index.js';\nimport type { ActivityType } from './types/activity.js';\nimport type {\n AiCritique,\n AiCritiqueField,\n AiCritiqueFinding,\n AiCritiqueKind,\n AiCritiqueRequest,\n AiRefusal,\n} from './types/ai.js';\n\n/** The most findings one critique may carry. More is refused, not cut. */\nexport const AI_CRITIQUE_MAX_FINDINGS = 20;\n\n/** The longest message or quote in a finding, in code points. */\nexport const AI_CRITIQUE_MAX_FIELD_LENGTH = 500;\n\nconst KINDS: readonly string[] = [\n 'ambiguous',\n 'second-answer',\n 'wrong-key',\n 'implausible-distractor',\n 'cue',\n 'level',\n 'language',\n 'sensitivity',\n 'other',\n] satisfies readonly AiCritiqueKind[];\n\n/**\n * Keys whose values are settings, ids or addresses — nothing a critique of the\n * item's words is about. A string under any of them is not a field to point at.\n */\nconst NOT_WORDS: ReadonlySet<string> = new Set([\n 'id',\n 'type',\n 'schemaVersion',\n 'mode',\n 'scoringStrategy',\n 'presentation',\n 'locale',\n 'languageTarget',\n 'correctChoiceId',\n 'bankId',\n 'url',\n 'captionsUrl',\n // A caption track's address and language, and a video's still: addresses\n // and settings, as `url` is.\n 'src',\n 'srclang',\n 'poster',\n // Who speaks a turn of a recording's script: a label for casting, not words.\n 'speaker',\n 'match',\n 'playback',\n 'recording',\n 'scoring',\n 'ai',\n 'learningObjectives',\n 'hints',\n 'kind',\n 'timeline',\n]);\n\n/**\n * Every string an author wrote into an item, with its path: the instructions,\n * the question, the options, the passage, the answers, the hints, the\n * feedback, a picture's `alt`. Settings, ids and addresses are left out, and\n * so is any `…Html` sidecar: the plain field beside it holds the same words.\n */\nfunction fieldsOf(value: unknown, path: string[] = []): AiCritiqueField[] {\n if (typeof value === 'string') {\n return value.trim() === '' ? [] : [{ path, text: value }];\n }\n if (Array.isArray(value)) {\n return value.flatMap((entry, index) => fieldsOf(entry, [...path, String(index)]));\n }\n if (typeof value === 'object' && value !== null) {\n return Object.entries(value).flatMap(([key, entry]) =>\n NOT_WORDS.has(key) || key.endsWith('Html') ? [] : fieldsOf(entry, [...path, key]),\n );\n }\n return [];\n}\n\n/**\n * What a model is given to review an item, or `null` when there is nothing to\n * review: a draft that is not an object, or one with no words in it yet.\n *\n * The facts are the item as the author wrote it — the answer key included; this\n * is for an author, never a learner — every text field a finding may point at,\n * and what the SDK's own critic already found there, so a model spends its\n * words on what a rule cannot see: a stem that reads two ways, a distractor\n * that is defensibly right, a key that is wrong, language above the level.\n *\n * Works for every registered type, the SDK's own and yours: the fields are read\n * from the draft itself.\n *\n * @throws UnknownActivityTypeError when `type` has no registered descriptor.\n */\nexport function aiCritiqueRequest(input: {\n type: ActivityType;\n draft: unknown;\n level?: string;\n authorLocale?: string;\n}): AiCritiqueRequest | null {\n const { type, draft, level, authorLocale } = input;\n if (getActivityTypeDescriptor(type) === undefined) {\n throw new UnknownActivityTypeError(String(type));\n }\n if (typeof draft !== 'object' || draft === null || Array.isArray(draft)) {\n return null;\n }\n const fields = fieldsOf(draft);\n if (fields.length === 0) {\n return null;\n }\n return {\n feature: 'item-critique',\n facts: {\n activityType: String(type),\n item: draft as Readonly<Record<string, unknown>>,\n fields,\n findings: critiqueDraft(type, draft).map(({ path, code, message }) => ({\n path,\n code,\n message,\n })),\n ...(level !== undefined ? { level } : {}),\n },\n ...(authorLocale !== undefined ? { authorLocale } : {}),\n };\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst tooLong = (text: string): boolean => [...text].length > AI_CRITIQUE_MAX_FIELD_LENGTH;\n\n/**\n * Checks a critique a port returned for `request`, and turns it into findings\n * an editor lists beside the SDK's own. As every AI answer, it is shown whole\n * or not at all.\n *\n * - **Every finding points at a field the item has.** Its `path` is one of\n * `facts.fields`, exactly; a path to a field the item does not have refuses\n * the whole reply as `contradicts-item`.\n * - **A quote is words that field contains**, typographic quotes and runs of\n * spacing aside, case kept; one it does not refuses the reply the same way.\n * - **Every finding is `advice`.** It is a model's opinion about the item, and\n * nothing in the SDK acts on it; its code is `ai_` and the kind\n * (`ai_second_answer`).\n * - **Limits:** {@link AI_CRITIQUE_MAX_FINDINGS} findings; a message or quote of\n * {@link AI_CRITIQUE_MAX_FIELD_LENGTH} code points; over them `too-long`. A\n * kind the SDK does not name, or any other shape, is `malformed`.\n */\nexport function checkAiCritique(\n raw: unknown,\n request: AiCritiqueRequest,\n): { ok: true; critique: AiCritique } | { ok: false; refusal: AiRefusal } {\n if (!isRecord(raw) || !Array.isArray(raw.findings)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (raw.findings.length > AI_CRITIQUE_MAX_FINDINGS) {\n return { ok: false, refusal: 'too-long' };\n }\n const fields = new Map(request.facts.fields.map((field) => [JSON.stringify(field.path), field]));\n const findings: AiCritiqueFinding[] = [];\n for (const entry of raw.findings) {\n if (\n !isRecord(entry) ||\n !Array.isArray(entry.path) ||\n !entry.path.every((segment) => typeof segment === 'string' || typeof segment === 'number') ||\n typeof entry.kind !== 'string' ||\n !KINDS.includes(entry.kind) ||\n typeof entry.message !== 'string' ||\n (entry.quote !== undefined && entry.quote !== null && typeof entry.quote !== 'string')\n ) {\n return { ok: false, refusal: 'malformed' };\n }\n const message = cleanText(entry.message);\n const quote = typeof entry.quote === 'string' ? cleanText(entry.quote) : undefined;\n if (message === '' || quote === '') {\n return { ok: false, refusal: 'malformed' };\n }\n if (tooLong(message) || (quote !== undefined && tooLong(quote))) {\n return { ok: false, refusal: 'too-long' };\n }\n const field = fields.get(JSON.stringify(entry.path.map(String)));\n if (field === undefined) {\n return { ok: false, refusal: 'contradicts-item' };\n }\n if (quote !== undefined && !folded(field.text).chars.includes(needle(quote))) {\n return { ok: false, refusal: 'contradicts-item' };\n }\n findings.push({\n path: field.path,\n code: `ai_${entry.kind.replace(/-/g, '_')}`,\n message,\n severity: 'advice',\n ...(quote !== undefined ? { quote } : {}),\n });\n }\n const provenance = provenanceOf(raw.provenance);\n const usage = usageOf(raw.usage);\n return {\n ok: true,\n critique: {\n findings,\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n","import { provenanceOf, usageOf } from './ai-text.js';\nimport { containsWords, fold } from './answer-leak.js';\nimport { keyPositionFinding } from './authoring/critique.js';\nimport {\n critiqueDraft,\n critiqueItemGroupDraft,\n validateDraft,\n validateItemGroupDraft,\n} from './authoring/index.js';\nimport type { ActivityMedia } from './types/activity.js';\nimport type {\n AiDraftCaption,\n AiDraftProblem,\n AiDraftSettings,\n AiDraftSource,\n AiDrafts,\n AiDraftsCall,\n AiDraftsRequest,\n AiDraftsRun,\n AiDraftType,\n AiGeneratedDraft,\n AiRefusal,\n AiVideoDraft,\n} from './types/ai.js';\n\n/** The most drafts one request may ask for, and the most a reply may hold when it names no count. */\nexport const AI_DRAFTS_MAX_COUNT = 50;\n\n/** The most repair passes `generateDrafts` makes. */\nexport const AI_DRAFTS_MAX_REPAIRS = 3;\n\nconst DRAFT_TYPES: readonly string[] = [\n 'multiple-choice',\n 'fill-in-the-blanks',\n 'gap-select',\n 'dictation',\n 'read-aloud',\n 'written-response',\n] satisfies readonly AiDraftType[];\n\ntype Fields = Record<string, unknown>;\n\nconst isRecord = (value: unknown): value is Fields =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n// ── The shape a model writes ───────────────────────────────────────────\n\nconst text = (description: string) => ({ type: 'string', description });\n\nconst FEEDBACK = {\n type: 'object',\n description: 'Optional overall feedback, shown after answering.',\n properties: {\n correct: text('Shown when the learner passes.'),\n incorrect: text('Shown when the learner does not.'),\n },\n};\n\n/** Every field a model writes, and the shape of each. A name means one thing in every type that uses it. */\nconst FIELDS: Readonly<Record<string, Fields>> = {\n title: text(\n 'A short name for the item, shown to the learner above it. Never the answer, or any part of it.',\n ),\n question: text('The question, as plain text.'),\n mode: {\n type: 'string',\n enum: ['single', 'multi'],\n description: '`single`: exactly one option is right. `multi`: one or more are.',\n },\n options: {\n type: 'array',\n minItems: 2,\n maxItems: 26,\n items: {\n type: 'object',\n required: ['text', 'isCorrect'],\n properties: {\n text: text('The option as the learner reads it.'),\n isCorrect: { type: 'boolean' },\n feedback: text('Why this option is right or wrong, shown after answering.'),\n },\n },\n },\n passage: text(\n 'The passage as plain text, with {{1}}, {{2}}, … where each gap goes, numbered in the order they appear.',\n ),\n blanks: {\n type: 'array',\n minItems: 1,\n description: 'One per gap, in the order the gaps appear: the first is {{1}}.',\n items: {\n type: 'object',\n required: ['acceptedAnswers'],\n properties: {\n acceptedAnswers: {\n type: 'array',\n minItems: 1,\n items: { type: 'string' },\n description: 'Every answer that is right, spelled as a learner would type it.',\n },\n hint: text('Optional. Helps without giving the answer or any of its words.'),\n },\n },\n },\n gaps: {\n type: 'array',\n minItems: 1,\n description: 'One per gap, in the order the gaps appear: the first is {{1}}.',\n items: {\n type: 'object',\n required: ['choices'],\n properties: {\n choices: {\n type: 'array',\n minItems: 2,\n description: 'What the learner chooses from. Exactly one is right.',\n items: {\n type: 'object',\n required: ['text', 'isCorrect'],\n properties: { text: text('A choice.'), isCorrect: { type: 'boolean' } },\n },\n },\n },\n },\n },\n transcript: text(\n 'What the learner hears and types: words from the source, as they are said there.',\n ),\n referenceText: text('What the learner reads aloud: a sentence or two from the source.'),\n instructions: text(\n 'Optional. The task, shown to the learner before the item: what to do (\"Complete the sentence. Use ONE word.\"), or how to read it aloud. Never part of the text the learner answers.',\n ),\n prompt: text('The writing task, as plain text.'),\n minWords: { type: 'integer', minimum: 0 },\n maxWords: { type: 'integer', minimum: 1 },\n rubric: {\n type: 'object',\n description: 'Optional. What the writing is assessed on; every criterion is weighted equally.',\n properties: {\n criteria: {\n type: 'array',\n minItems: 1,\n items: {\n type: 'object',\n required: ['name'],\n properties: { name: text('A short name.'), description: text('What it looks for.') },\n },\n },\n },\n },\n feedback: FEEDBACK,\n};\n\n/** The fields a model writes for each type: a draft's own names, never an id or a setting. */\nconst WRITES: Readonly<Record<AiDraftType, readonly string[]>> = {\n 'multiple-choice': ['title', 'instructions', 'question', 'mode', 'options', 'feedback'],\n 'fill-in-the-blanks': ['title', 'instructions', 'passage', 'blanks', 'feedback'],\n 'gap-select': ['title', 'instructions', 'passage', 'gaps', 'feedback'],\n dictation: ['title', 'transcript', 'feedback'],\n 'read-aloud': ['title', 'referenceText', 'instructions', 'feedback'],\n 'written-response': ['title', 'prompt', 'minWords', 'maxWords', 'rubric'],\n};\n\n/**\n * One draft, as a model writes it: its `type`, and the fields that type uses —\n * one object for every chosen type, since a structured output that must pick\n * between schemas is the thing providers support least.\n */\nfunction itemShape(types: readonly AiDraftType[], captions: boolean): Fields {\n const properties: Fields = {\n type: {\n type: 'string',\n enum: [...types],\n description: `The item's type, and the fields it fills. ${types\n .map((type) => `${type}: ${WRITES[type].filter((name) => name !== 'title').join(', ')}`)\n .join('. ')}.`,\n },\n };\n for (const type of types) {\n for (const name of WRITES[type]) {\n properties[name] = FIELDS[name];\n }\n }\n if (captions) {\n properties.caption = {\n type: 'integer',\n minimum: 0,\n description: 'The index of the caption the item is about. It opens when that caption ends.',\n };\n }\n return {\n type: 'object',\n required: captions ? ['type', 'title', 'caption'] : ['type', 'title'],\n properties,\n };\n}\n\n/** A reply of up to `max` drafts — exactly `exactly` on a repair. */\nfunction replyShape(\n types: readonly AiDraftType[],\n captions: boolean,\n max: number,\n exactly?: number,\n): Fields {\n return {\n type: 'object',\n required: ['drafts'],\n properties: {\n drafts: {\n type: 'array',\n ...(exactly !== undefined ? { minItems: exactly, maxItems: exactly } : { maxItems: max }),\n items: itemShape(types, captions),\n },\n },\n };\n}\n\n// ── The request ────────────────────────────────────────────────────────\n\n/**\n * What a model is given to draft items from a source, or `null` when there is\n * no source to draft from: an empty text, or no caption with words in it.\n *\n * `types` are the ones the author chose, in the author's order: the model\n * leans on the first, and where several drafts share a moment of a video — or\n * the end of one — they are shown in this order. `count` is how many to write\n * at most; left out, the model writes as many as the source is worth, up to\n * {@link AI_DRAFTS_MAX_COUNT}.\n *\n * The facts are the source — a caption list numbered, with its times in\n * seconds — the types, the count, and the language, level and instructions\n * the author gave. `shape` is the JSON Schema of the reply: each draft names\n * its type and writes that type's own fields, with no ids, no HTML, no media\n * and no settings. Those are not a model's to write. `settings` are yours, put\n * on every draft of a type: a read-aloud's `recording` and `scoring`, say, which\n * the SDK has no default for.\n *\n * @throws RangeError when `types` is empty, repeats a type or names one a model\n * does not draft, or `count` is not a whole number from 1 to\n * {@link AI_DRAFTS_MAX_COUNT}.\n */\nexport function aiDraftsRequest(input: {\n types: readonly AiDraftType[];\n source: AiDraftSource;\n count?: number;\n locale?: string;\n level?: string;\n instructions?: string;\n settings?: AiDraftSettings;\n}): AiDraftsRequest | null {\n const { types, source, count, locale, level, instructions, settings } = input;\n if (\n !Array.isArray(types) ||\n types.length === 0 ||\n new Set(types).size !== types.length ||\n !types.every((type) => DRAFT_TYPES.includes(type))\n ) {\n throw new RangeError(\n `aiDraftsRequest: types must be one or more of ${DRAFT_TYPES.join(', ')}, each once, in the author's order.`,\n );\n }\n if (\n count !== undefined &&\n (!Number.isInteger(count) || count < 1 || count > AI_DRAFTS_MAX_COUNT)\n ) {\n throw new RangeError(\n `aiDraftsRequest: count must be a whole number from 1 to ${AI_DRAFTS_MAX_COUNT}, or left out, not ${String(count)}.`,\n );\n }\n let facts: AiDraftsRequest['facts']['source'];\n if (source.kind === 'captions') {\n const captions: AiDraftCaption[] = (Array.isArray(source.cues) ? source.cues : []).flatMap(\n (cue, index) =>\n isRecord(cue) &&\n typeof cue.text === 'string' &&\n cue.text.trim() !== '' &&\n Number.isFinite(cue.start) &&\n Number.isFinite(cue.end)\n ? [{ index, start: cue.start, end: cue.end, text: cue.text }]\n : [],\n );\n if (captions.length === 0) {\n return null;\n }\n facts = { kind: 'captions', captions };\n } else {\n if (typeof source.text !== 'string' || source.text.trim() === '') {\n return null;\n }\n facts = { kind: source.kind, text: source.text };\n }\n return {\n feature: 'draft-generation',\n facts: {\n activityTypes: [...types],\n source: facts,\n ...(count !== undefined ? { count } : {}),\n ...(locale !== undefined ? { locale } : {}),\n ...(level !== undefined ? { level } : {}),\n ...(instructions !== undefined ? { instructions } : {}),\n },\n shape: replyShape(types, facts.kind === 'captions', count ?? AI_DRAFTS_MAX_COUNT),\n ...(settings !== undefined ? { settings } : {}),\n };\n}\n\n// ── From a reply to drafts ─────────────────────────────────────────────\n\nconst stringOf = (value: unknown): string => (typeof value === 'string' ? value : '');\nconst listOf = (value: unknown): readonly unknown[] => (Array.isArray(value) ? value : []);\nconst written = (value: unknown): string | undefined =>\n typeof value === 'string' && value.trim() !== '' ? value : undefined;\n\nfunction feedbackOf(entry: Fields): Fields {\n if (!isRecord(entry.feedback)) {\n return {};\n }\n const correct = written(entry.feedback.correct);\n const incorrect = written(entry.feedback.incorrect);\n return correct === undefined && incorrect === undefined\n ? {}\n : {\n feedback: {\n ...(correct !== undefined ? { correct } : {}),\n ...(incorrect !== undefined ? { incorrect } : {}),\n },\n };\n}\n\n/** The instructions a model wrote for the learner, when it wrote any. */\nfunction instructionsOf(entry: Fields): Fields {\n const instructions = written(entry.instructions);\n return instructions !== undefined ? { instructions } : {};\n}\n\n/** A type's content, read from what a model wrote. */\nfunction contentOf(type: AiDraftType, entry: Fields, newId: () => string): Fields {\n switch (type) {\n case 'multiple-choice':\n return {\n ...instructionsOf(entry),\n question: stringOf(entry.question),\n ...(typeof entry.mode === 'string' ? { mode: entry.mode } : {}),\n options: listOf(entry.options).map((option) => {\n const fields = isRecord(option) ? option : {};\n const feedback = written(fields.feedback);\n return {\n id: newId(),\n text: stringOf(fields.text),\n isCorrect: fields.isCorrect === true,\n ...(feedback !== undefined ? { feedback } : {}),\n };\n }),\n ...feedbackOf(entry),\n };\n case 'fill-in-the-blanks':\n return {\n ...instructionsOf(entry),\n passage: stringOf(entry.passage),\n blanks: listOf(entry.blanks).map((blank, index) => {\n const fields = isRecord(blank) ? blank : {};\n const hint = written(fields.hint);\n return {\n id: String(index + 1),\n acceptedAnswers: listOf(fields.acceptedAnswers).filter(\n (answer): answer is string => typeof answer === 'string',\n ),\n ...(hint !== undefined ? { hint } : {}),\n };\n }),\n ...feedbackOf(entry),\n };\n case 'gap-select':\n return {\n ...instructionsOf(entry),\n passage: stringOf(entry.passage),\n gaps: listOf(entry.gaps).map((gap, index) => {\n const marked = listOf(isRecord(gap) ? gap.choices : undefined);\n const choices = marked.map((choice) => ({\n id: newId(),\n text: stringOf(isRecord(choice) ? choice.text : undefined),\n }));\n // Exactly one marked right is a key; none, or several, is no key —\n // which validateDraft asks for, and a repair then supplies.\n const right = marked.flatMap((choice, at) =>\n isRecord(choice) && choice.isCorrect === true ? [at] : [],\n );\n return {\n id: String(index + 1),\n choices,\n correctChoiceId: right.length === 1 ? choices[right[0] as number]?.id : '',\n };\n }),\n ...feedbackOf(entry),\n };\n case 'dictation':\n return { transcript: stringOf(entry.transcript), ...feedbackOf(entry) };\n case 'read-aloud':\n return {\n referenceText: stringOf(entry.referenceText),\n ...instructionsOf(entry),\n ...feedbackOf(entry),\n };\n default: {\n const criteria = listOf(isRecord(entry.rubric) ? entry.rubric.criteria : undefined);\n return {\n prompt: stringOf(entry.prompt),\n ...(entry.minWords !== undefined ? { minWords: entry.minWords } : {}),\n ...(entry.maxWords !== undefined ? { maxWords: entry.maxWords } : {}),\n ...(criteria.length > 0\n ? {\n rubric: {\n criteria: criteria.map((criterion) => {\n const fields = isRecord(criterion) ? criterion : {};\n const description = written(fields.description);\n return {\n name: stringOf(fields.name),\n ...(description !== undefined ? { description } : {}),\n weight: 1,\n };\n }),\n },\n }\n : {}),\n };\n }\n }\n}\n\n/**\n * What a new draft of a type starts with, where the model writes nothing: a\n * scoring strategy on the three that have one — `all-or-nothing`, as a new\n * draft has — and a read-aloud's recording and scoring left unset, as a new\n * draft leaves them, for the host's settings or the author to fill.\n */\nfunction startOf(type: AiDraftType): Fields {\n switch (type) {\n case 'multiple-choice':\n case 'fill-in-the-blanks':\n case 'gap-select':\n return { scoringStrategy: 'all-or-nothing' };\n case 'read-aloud':\n return { recording: { maxSeconds: 0 }, scoring: { dimensions: [] } };\n default:\n return {};\n }\n}\n\n/**\n * A draft from what a model wrote. Lenient: every field it can read is kept as\n * written, and anything it cannot is left for `validateDraft` to name — which\n * is what a repair tells the model. Ids are the host's; a blank's and a gap's\n * are the numbers of their placeholders, as the passage names them. In order:\n * what a new draft starts with, the language asked for, the host's settings,\n * then what the model wrote — so a setting never overrides the content, and\n * nothing overrides what the draft is.\n */\nfunction draftFrom(\n type: AiDraftType,\n entry: Fields,\n id: string,\n newId: () => string,\n request: AiDraftsRequest,\n): Fields {\n const locale = request.facts.locale;\n return {\n ...startOf(type),\n ...(locale !== undefined ? { locale } : {}),\n ...request.settings?.[type],\n title: stringOf(entry.title),\n ...contentOf(type, entry, newId),\n schemaVersion: '1.0',\n type,\n id,\n };\n}\n\n/** Ids from the host, checked as `createDraft` checks them. */\nfunction idSource(newId: () => string, caller: string): () => string {\n const issued = new Set<string>();\n return () => {\n const id: unknown = newId();\n if (typeof id !== 'string' || id === '') {\n throw new Error(`${caller}: newId() must return a non-empty string.`);\n }\n if (issued.has(id)) {\n throw new Error(\n `${caller}: newId() returned \"${id}\" twice. Ids must differ — two options sharing an id cannot be scored apart.`,\n );\n }\n issued.add(id);\n return id;\n };\n}\n\n/** Drafts in the author's order of types, and within a type in the order the model wrote them. */\nfunction inAuthorsOrder(\n drafts: readonly AiGeneratedDraft[],\n types: readonly AiDraftType[],\n): AiGeneratedDraft[] {\n return [...drafts].sort(\n (a, b) => types.indexOf(a.type) - types.indexOf(b.type) || a.index - b.index,\n );\n}\n\nfunction check(\n raw: unknown,\n request: AiDraftsRequest,\n newId: () => string,\n keep: readonly (string | undefined)[] = [],\n): { ok: true; drafts: AiDrafts } | { ok: false; refusal: AiRefusal } {\n const types = request.facts.activityTypes;\n if (\n !isRecord(raw) ||\n !Array.isArray(raw.drafts) ||\n !raw.drafts.every((entry) => isRecord(entry) && types.includes(entry.type as AiDraftType))\n ) {\n return { ok: false, refusal: 'malformed' };\n }\n const entries = raw.drafts as Fields[];\n const repair = request.repair;\n const most = request.facts.count ?? AI_DRAFTS_MAX_COUNT;\n if (repair !== undefined ? entries.length !== repair.length : entries.length > most) {\n return { ok: false, refusal: repair !== undefined ? 'malformed' : 'too-long' };\n }\n const source = request.facts.source;\n const drafts = entries.map((entry, position): AiGeneratedDraft => {\n const type = entry.type as AiDraftType;\n const draft = draftFrom(type, entry, keep[position] ?? newId(), newId, request);\n const caption =\n source.kind === 'captions' && Number.isInteger(entry.caption)\n ? source.captions.find((one) => one.index === entry.caption)\n : undefined;\n // A dictation's recording can be cut from the video only where the video\n // says its words: the caption it named, holding its transcript.\n const said = fold(stringOf(entry.transcript));\n const clip =\n type === 'dictation' && caption !== undefined && containsWords(fold(caption.text), said)\n ? { start: caption.start, end: caption.end }\n : undefined;\n return {\n index: repair !== undefined ? (repair[position]?.index as number) : position,\n type,\n draft,\n validation: validateDraft(type, draft),\n findings: critiqueDraft(type, draft),\n ...(caption !== undefined ? { at: caption.end } : {}),\n ...(clip !== undefined ? { clip } : {}),\n generated: entry,\n };\n });\n const ordered = inAuthorsOrder(drafts, types);\n const provenance = provenanceOf(raw.provenance);\n const usage = usageOf(raw.usage);\n return {\n ok: true,\n drafts: {\n drafts: ordered,\n findings: keyPositionFinding(\n ordered.map((one) => one.draft),\n [],\n ),\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n\n/**\n * Reads a model's reply to {@link aiDraftsRequest} into drafts an editor can\n * show for approval: each with ids from `newId`, your settings for its type,\n * what `validateDraft` says of it, what the item critic finds in it, and — from\n * captions — where it goes, and for a dictation the stretch of the video that\n * says its words. They come back in the author's order of types.\n *\n * Drafts are independent: one that is unfinished or wrong is returned as such,\n * for an author or {@link aiDraftsRepairRequest} to fix, beside the ones that\n * are complete. `complete` means valid, never approved: a model wrote it, and a\n * person decides whether learners see it. The whole reply is refused only when\n * it is not `{ drafts: [...] }` of objects of the chosen types (`malformed`), or\n * holds more drafts than asked for (`too-long`) — on a repair, not exactly one\n * per draft to fix (`malformed`).\n *\n * Only the fields a type's shape names are read: a model cannot set an id,\n * HTML, media, a switch or a scoring setting through this. A question is scored\n * `all-or-nothing` unless your settings say otherwise, a written response's\n * criteria are weighted equally, and a dictation carries no recording — attach\n * one, or cut it from the video at `clip`.\n *\n * @throws Error when `newId` returns an empty or repeated id.\n */\nexport function checkAiDrafts(\n raw: unknown,\n request: AiDraftsRequest,\n options: { newId: () => string },\n): { ok: true; drafts: AiDrafts } | { ok: false; refusal: AiRefusal } {\n return check(raw, request, idSource(options.newId, 'checkAiDrafts'));\n}\n\n// ── Repair ─────────────────────────────────────────────────────────────\n\n/**\n * Where a model wrote what a problem names: the draft's paths, but a gap's key\n * is its choices, and a reading too long for the host's recording time is the\n * text the model chose.\n */\nfunction pathForModel(type: AiDraftType, problem: AiDraftProblem): string[] {\n const { path, code } = problem;\n if (\n type === 'gap-select' &&\n path.length === 3 &&\n path[0] === 'gaps' &&\n path[2] === 'correctChoiceId'\n ) {\n return ['gaps', path[1] as string, 'choices'];\n }\n return code === 'ra_text_long_for_time' ? ['referenceText'] : [...path];\n}\n\n/**\n * What is wrong with a draft that the model can fix: every issue and warning\n * at a field the model writes. A read-aloud with no recording time, or a\n * dictation in a video with no recording, is the host's to finish, and is not\n * sent back.\n */\nfunction problemsOf(one: AiGeneratedDraft): AiDraftProblem[] {\n const writes = WRITES[one.type];\n return [\n ...one.validation.issues.map(\n (found): AiDraftProblem => ({\n path: found.path,\n code: found.code,\n message: found.message,\n severity: found.severity,\n }),\n ),\n ...one.findings\n .filter((found) => found.severity === 'warning')\n .map(\n (found): AiDraftProblem => ({\n path: found.path,\n code: found.code,\n message: found.message,\n severity: 'warning',\n }),\n ),\n ]\n .map((problem) => ({ ...problem, path: pathForModel(one.type, problem) }))\n .filter((problem) => writes.includes(problem.path[0] as string));\n}\n\n/**\n * A second request, for the drafts with something wrong the model can fix —\n * unfinished, wrong, or a flaw the critic warns about, at a field the model\n * writes — each as the model wrote it, with those problems at the paths the\n * model wrote. `null` when there is none.\n *\n * The reply holds one draft for each, in order; {@link checkAiDrafts} reads it\n * and gives each the `index` of the draft it replaces.\n */\nexport function aiDraftsRepairRequest(\n request: AiDraftsRequest,\n drafts: AiDrafts | readonly AiGeneratedDraft[],\n): AiDraftsRequest | null {\n const list: readonly AiGeneratedDraft[] = 'drafts' in drafts ? drafts.drafts : drafts;\n const repair = list.flatMap((one) => {\n const problems = problemsOf(one);\n return problems.length === 0 ? [] : [{ index: one.index, draft: one.generated, problems }];\n });\n if (repair.length === 0) {\n return null;\n }\n const { repair: _previous, ...first } = request;\n return {\n ...first,\n shape: replyShape(\n request.facts.activityTypes,\n request.facts.source.kind === 'captions',\n repair.length,\n repair.length,\n ),\n repair,\n };\n}\n\n// ── The loop ───────────────────────────────────────────────────────────\n\nconst RANK = { complete: 2, incomplete: 1, invalid: 0 } as const;\n\n/** Whether a repaired draft is at least as good as the one it would replace. */\nfunction noWorse(repaired: AiGeneratedDraft, before: AiGeneratedDraft): boolean {\n const warnings = (one: AiGeneratedDraft): number =>\n one.findings.filter((found) => found.severity === 'warning').length;\n const now = RANK[repaired.validation.status];\n const was = RANK[before.validation.status];\n return now > was || (now === was && warnings(repaired) <= warnings(before));\n}\n\n/**\n * Drafts items from a source, with a bounded repair loop: asks the port with\n * `request`, reads the reply with {@link checkAiDrafts}, then — up to `repairs`\n * times — sends back the drafts with something wrong the model can fix\n * ({@link aiDraftsRepairRequest}), and keeps a repaired draft only when it is\n * no worse than the one it replaces. A repaired draft keeps its id.\n *\n * Never throws for a model's failure: a port that throws or a reply the SDK\n * refuses ends the loop, and is recorded in `calls` beside the provenance and\n * usage of every call — which the SDK never adds up. A first call that fails\n * leaves no drafts.\n *\n * ```ts\n * const run = await generateDrafts({\n * request: aiDraftsRequest({ types: ['multiple-choice', 'dictation'], source: { kind: 'transcript', text } }),\n * port: (request) => callMyModel(DRAFTS_PROMPT, request),\n * newId: () => crypto.randomUUID(),\n * });\n * // run.drafts: store each as proposed, for a person to approve.\n * ```\n *\n * @throws RangeError when `repairs` is not a whole number from 0 to\n * {@link AI_DRAFTS_MAX_REPAIRS} (default 1), and Error when `newId` returns an\n * empty or repeated id.\n */\nexport async function generateDrafts(input: {\n request: AiDraftsRequest;\n port: (request: AiDraftsRequest) => Promise<unknown> | unknown;\n newId: () => string;\n repairs?: number;\n}): Promise<AiDraftsRun> {\n const { request, port, newId } = input;\n const repairs = input.repairs ?? 1;\n if (!Number.isInteger(repairs) || repairs < 0 || repairs > AI_DRAFTS_MAX_REPAIRS) {\n throw new RangeError(\n `generateDrafts: repairs must be a whole number from 0 to ${AI_DRAFTS_MAX_REPAIRS}, not ${String(repairs)}.`,\n );\n }\n const calls: AiDraftsCall[] = [];\n // One source of ids for the whole run, so no id is issued twice across passes.\n const ids = idSource(newId, 'generateDrafts');\n\n const ask = async (\n asked: AiDraftsRequest,\n keep: readonly (string | undefined)[],\n ): Promise<AiDrafts | undefined> => {\n let raw: unknown;\n try {\n raw = await port(asked);\n } catch (error) {\n calls.push({ ok: false, error: error instanceof Error ? error.message : String(error) });\n return undefined;\n }\n const checked = check(raw, asked, ids, keep);\n if (!checked.ok) {\n calls.push({ ok: false, refusal: checked.refusal });\n return undefined;\n }\n const { provenance, usage } = checked.drafts;\n calls.push({\n ok: true,\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n });\n return checked.drafts;\n };\n\n const first = await ask(request, []);\n if (first === undefined) {\n return { drafts: [], findings: [], calls };\n }\n let current = first.drafts;\n for (let pass = 0; pass < repairs; pass += 1) {\n const repairRequest = aiDraftsRepairRequest(request, current);\n if (repairRequest === null) {\n break;\n }\n const keep = (repairRequest.repair ?? []).map((wanted) => {\n const before = current.find((one) => one.index === wanted.index);\n return typeof before?.draft.id === 'string' ? before.draft.id : undefined;\n });\n const repaired = await ask(repairRequest, keep);\n if (repaired === undefined) {\n break;\n }\n current = current.map((before) => {\n const after = repaired.drafts.find((one) => one.index === before.index);\n return after !== undefined && noWorse(after, before) ? after : before;\n });\n }\n return {\n drafts: current,\n findings: keyPositionFinding(\n current.map((one) => one.draft),\n [],\n ),\n calls,\n };\n}\n\n// ── An interactive video ───────────────────────────────────────────────\n\n/** The source as one text, kept with the video as its author-only transcript. */\nfunction sourceText(request: AiDraftsRequest): string {\n const source = request.facts.source;\n return source.kind === 'captions'\n ? source.captions.map((caption) => caption.text).join('\\n')\n : source.text;\n}\n\n/**\n * An interactive video made of drafts: an item group whose stimulus is\n * `video`, whose items are the drafts, and whose timeline opens a quiz at each\n * moment a draft was placed — drafts placed at one moment share its quiz, in\n * the author's order of types — and one more at `durationSeconds` for every\n * draft placed nowhere: all of them when the source had no times.\n *\n * The SDK never reads the video, so it cannot know where the end is: without\n * `durationSeconds` that last quiz has no time yet, which\n * `validateItemGroupDraft` reports for the author to set. The source is kept on\n * the stimulus as its author-only `transcript`.\n *\n * Like every draft it is checked (`validateItemGroupDraft`) and critiqued\n * (`critiqueItemGroupDraft`), never approved. A dictation still needs its own\n * recording; a type a video does not take — a written response — is reported,\n * not dropped.\n *\n * @throws Error when `newId` returns an empty or repeated id.\n */\nexport function interactiveVideoFromDrafts(input: {\n request: AiDraftsRequest;\n drafts: AiDrafts | AiDraftsRun | readonly AiGeneratedDraft[];\n video: ActivityMedia;\n durationSeconds?: number;\n title?: string;\n newId: () => string;\n}): AiVideoDraft {\n const { request, drafts, video, durationSeconds, title } = input;\n const ids = idSource(input.newId, 'interactiveVideoFromDrafts');\n const types = request.facts.activityTypes;\n const list: readonly AiGeneratedDraft[] = 'drafts' in drafts ? drafts.drafts : drafts;\n const end =\n typeof durationSeconds === 'number' && Number.isFinite(durationSeconds) && durationSeconds >= 0\n ? durationSeconds\n : undefined;\n // When each draft opens: its caption's end, or the end of the video.\n const placed = list.map((one) => ({ one, at: one.at ?? end }));\n const ordered = [...placed].sort(\n (a, b) =>\n (a.at ?? Number.POSITIVE_INFINITY) - (b.at ?? Number.POSITIVE_INFINITY) ||\n types.indexOf(a.one.type) - types.indexOf(b.one.type) ||\n a.one.index - b.one.index,\n );\n const cues: { id: string; at?: number; itemIds: string[] }[] = [];\n for (const { one, at } of ordered) {\n const itemId = String(one.draft.id);\n const last = cues[cues.length - 1];\n if (last !== undefined && last.at === at) {\n last.itemIds.push(itemId);\n } else {\n cues.push({ id: ids(), ...(at !== undefined ? { at } : {}), itemIds: [itemId] });\n }\n }\n const group = {\n schemaVersion: '1.0',\n type: 'item-group',\n id: ids(),\n ...(title !== undefined ? { title } : {}),\n stimulus: { id: ids(), kind: 'video', media: video, transcript: sourceText(request) },\n items: ordered.map(({ one }) => one.draft),\n timeline: { cues },\n };\n return {\n group,\n validation: validateItemGroupDraft(group),\n findings: critiqueItemGroupDraft(group),\n };\n}\n","import { cleanText, folded, needle } from './ai-text.js';\nimport type { AiRefusal } from './types/ai.js';\nimport type { InlineCorrection } from './types/grading.js';\n\n/**\n * What the AI features about a written response share: reading a model's text\n * fields, and finding each correction's quote in the learner's text.\n *\n * Internal. `ai-writing.ts` (feedback on a draft) and `ai-grading.ts` (a\n * grade) are their public faces, and hold every correction to the same\n * anchoring.\n */\n\n/** The most corrections one piece of writing feedback may carry. More is refused, not cut. */\nexport const AI_WRITING_MAX_CORRECTIONS = 20;\n\n/** The longest quote, correction, explanation or criterion comment, in code points. */\nexport const AI_WRITING_MAX_FIELD_LENGTH = 500;\n\n/** The longest `category` or `band`: a tag, not a sentence. */\nexport const MAX_TAG_LENGTH = 40;\n\n// ── Finding a quote in the draft ───────────────────────────────────────\n\n/**\n * Where `quote` sits in the draft: at `claimed`, when the model named a range —\n * which must then hold the quote — or else at its first occurrence not already\n * taken by an identical quote, so a mistake made twice is anchored twice. A\n * quote corrected more often than it occurs takes its first occurrence again.\n * `null` when the draft does not contain it where claimed, or at all.\n */\nfunction anchor(\n draft: { text: string; chars: string; from: number[] },\n quote: string,\n claimed: { start: number; end: number } | undefined,\n taken: Set<string>,\n): { start: number; end: number } | null {\n const sought = needle(quote);\n if (sought === '') {\n return null;\n }\n if (claimed !== undefined) {\n const { start, end } = claimed;\n if (\n !Number.isSafeInteger(start) ||\n !Number.isSafeInteger(end) ||\n start < 0 ||\n end > draft.text.length ||\n start >= end\n ) {\n return null;\n }\n const span = draft.text.slice(start, end);\n if (needle(span) !== sought) {\n return null;\n }\n // The span, without whitespace the model's range took in at either end.\n const lead = span.length - span.trimStart().length;\n const trail = span.length - span.trimEnd().length;\n return { start: start + lead, end: end - trail };\n }\n let first: { start: number; end: number } | null = null;\n for (let at = draft.chars.indexOf(sought); at !== -1; at = draft.chars.indexOf(sought, at + 1)) {\n const start = draft.from[at] as number;\n const end = (draft.from[at + sought.length - 1] as number) + 1;\n const found = { start, end };\n first ??= found;\n const key = `${start}:${end}`;\n if (!taken.has(key)) {\n taken.add(key);\n return found;\n }\n }\n return first;\n}\n\n// ── Checking what comes back ───────────────────────────────────────────\n\n/** A field that must be a string, at most `max` code points once cleaned; `undefined` when absent. */\nexport function textField(\n value: unknown,\n max: number,\n): { ok: true; value: string | undefined } | { ok: false; refusal: AiRefusal } {\n if (value === undefined || value === null) {\n return { ok: true, value: undefined };\n }\n if (typeof value !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n const cleaned = cleanText(value);\n if ([...cleaned].length > max) {\n return { ok: false, refusal: 'too-long' };\n }\n return { ok: true, value: cleaned === '' ? undefined : cleaned };\n}\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/** The corrections, each anchored in the draft, or why the reply is refused. */\nexport function checkCorrections(\n raw: unknown,\n text: string,\n): { ok: true; corrections: InlineCorrection[] } | { ok: false; refusal: AiRefusal } {\n if (raw === undefined || raw === null) {\n return { ok: true, corrections: [] };\n }\n if (!Array.isArray(raw)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (raw.length > AI_WRITING_MAX_CORRECTIONS) {\n return { ok: false, refusal: 'too-long' };\n }\n const draft = { text, ...folded(text) };\n const taken = new Set<string>();\n const corrections: InlineCorrection[] = [];\n for (const entry of raw) {\n if (!isRecord(entry) || typeof entry.original !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n if (typeof entry.corrected !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n if (\n [...entry.original].length > AI_WRITING_MAX_FIELD_LENGTH ||\n [...entry.corrected].length > AI_WRITING_MAX_FIELD_LENGTH\n ) {\n return { ok: false, refusal: 'too-long' };\n }\n // A correction that changes nothing corrects nothing.\n if (needle(entry.original) === needle(entry.corrected)) {\n return { ok: false, refusal: 'malformed' };\n }\n const explanation = textField(entry.explanation, AI_WRITING_MAX_FIELD_LENGTH);\n const category = textField(entry.category, MAX_TAG_LENGTH);\n if (!explanation.ok) {\n return explanation;\n }\n if (!category.ok) {\n return category;\n }\n let claimed: { start: number; end: number } | undefined;\n if (entry.range !== undefined && entry.range !== null) {\n if (!isRecord(entry.range)) {\n return { ok: false, refusal: 'malformed' };\n }\n claimed = { start: entry.range.start as number, end: entry.range.end as number };\n }\n const at = anchor(draft, entry.original, claimed, taken);\n if (at === null) {\n return { ok: false, refusal: 'misquotes-answer' };\n }\n corrections.push({\n original: text.slice(at.start, at.end),\n corrected: cleanText(entry.corrected),\n ...(explanation.value !== undefined ? { explanation: explanation.value } : {}),\n ...(category.value !== undefined ? { category: category.value } : {}),\n range: at,\n });\n }\n return { ok: true, corrections };\n}\n","import { AI_TEXT_MAX_LENGTH } from './ai.js';\nimport { materialFacts } from './ai-material.js';\nimport { provenanceOf, usageOf } from './ai-text.js';\nimport {\n AI_WRITING_MAX_FIELD_LENGTH,\n checkCorrections,\n isRecord,\n textField,\n} from './ai-written.js';\nimport { countWords } from './count-words.js';\nimport {\n LEVEL_PROBABILITY_TOLERANCE,\n levelDistribution,\n levelKey,\n readRubricLevels,\n} from './rubric-levels.js';\nimport { SPEAKING_MEASURES } from './schemas/speaking-response.js';\nimport { validateSpeechAssessment } from './scoring/speech/assessment.js';\nimport type {\n LearnerResponse,\n SpeakingMeasure,\n SpeakingResponseData,\n WrittenResponseData,\n} from './types/activity.js';\nimport type {\n AiActivityInput,\n AiGradedCriterion,\n AiGradingCriterionFact,\n AiGradingJudgement,\n AiGradingRequest,\n AiGradingRequestProblem,\n AiGroupInput,\n AiRefusal,\n AiSpeakingCriterionFact,\n AiSpeakingGradingRequest,\n AiSpeakingGradingRequestProblem,\n AiSpeakingMeasureFact,\n} from './types/ai.js';\nimport type { SpeechAssessment } from './types/speech.js';\n\n/**\n * How far a model's level probabilities may sum from 1 and still be a\n * distribution: rounding in a model's output, not a different judgement.\n */\nexport const AI_GRADING_PROBABILITY_TOLERANCE = LEVEL_PROBABILITY_TOLERANCE;\n\n/**\n * The request for a model's grade of a submitted written response, or why there\n * is none to make.\n *\n * Grading happens on the server, from the whole item: the rubric is what a\n * model is held to, so an item without one — or one whose criteria are not each\n * a name and a weight — gives no request, nor does a `redact()` projection or\n * an empty response. A criterion's `levels`, when there are any, must be ones\n * the grading functions can read; levels they cannot read give no request\n * rather than a grade on a scale the author did not mean. `group` is the item\n * group the task sits in: its material goes with the request as `material`, as\n * on feedback on writing.\n *\n * The author's AI switch (`ai.explanations`) is not read: grading is the\n * deployment's decision, made on the server, not help a learner asks for.\n */\nexport function aiGradingRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n group?: AiGroupInput;\n learnerLocale?: string;\n}):\n | { ok: true; request: AiGradingRequest }\n | { ok: false; problem: AiGradingRequestProblem; path?: (string | number)[] } {\n const { data, response, learnerLocale } = input;\n if (data.type !== 'written-response') {\n return { ok: false, problem: 'not-written-response' };\n }\n if ((data as { redacted?: unknown }).redacted === true) {\n return { ok: false, problem: 'redacted' };\n }\n const item = data as unknown as WrittenResponseData;\n const rubric = rubricFacts(item.rubric);\n if (!rubric.ok) {\n return rubric;\n }\n const text = response?.type === 'written-response' ? response.text : undefined;\n if (typeof text !== 'string' || text.trim() === '') {\n return { ok: false, problem: 'empty-response' };\n }\n const wordCount = countWords(text);\n const minWords = typeof item.minWords === 'number' ? item.minWords : 0;\n const maxWords = typeof item.maxWords === 'number' ? item.maxWords : Number.POSITIVE_INFINITY;\n const material = materialFacts(input.group);\n return {\n ok: true,\n request: {\n feature: 'grading',\n facts: {\n activityType: 'written-response',\n activityId: data.id,\n title: data.title,\n ...(typeof data.locale === 'string' ? { locale: data.locale } : {}),\n prompt: typeof item.prompt === 'string' ? item.prompt : '',\n text,\n wordCount,\n minWords,\n maxWords,\n withinWordBounds: wordCount >= minWords && wordCount <= maxWords,\n rubric: rubric.criteria,\n ...(typeof item.languageTarget === 'string' ? { languageTarget: item.languageTarget } : {}),\n },\n ...(material !== null ? { material } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n },\n };\n}\n\nconst MEASURES: readonly unknown[] = SPEAKING_MEASURES;\n\n/**\n * The request for a model's grade of a spoken answer (1.9), or why there is\n * none to make.\n *\n * Built as a written response's is, on the server and from the whole item, with\n * the take's evidence: the model reads `assessment.transcript` — the\n * transcriber's verbatim text, the same text the engine measured the take\n * against — and scores the criteria a judge scores: those that name no\n * `measure`, or every criterion when `judgeMeasures` is set. The measured\n * criteria go with it as context, with the engine's scores, so feedback can\n * speak of them; the model does not score them.\n *\n * The evidence is checked as `gradeSpeakingResponse` checks it — valid,\n * unscripted, for this item's locale and this response's recording — so a model\n * is never paid to judge a take the grade would refuse. Read the reply with\n * `checkAiGrading`, and grade with `gradeSpeakingResponse` under the same\n * `judgeMeasures`.\n */\nexport function aiSpeakingGradingRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n assessment: SpeechAssessment | null;\n judgeMeasures?: boolean;\n group?: AiGroupInput;\n learnerLocale?: string;\n}):\n | { ok: true; request: AiSpeakingGradingRequest }\n | { ok: false; problem: AiSpeakingGradingRequestProblem; path?: (string | number)[] } {\n const { data, response, learnerLocale } = input;\n if (data.type !== 'speaking-response') {\n return { ok: false, problem: 'not-speaking-response' };\n }\n if ((data as { redacted?: unknown }).redacted === true) {\n return { ok: false, problem: 'redacted' };\n }\n const item = data as unknown as SpeakingResponseData;\n const rubric = rubricFacts(item.rubric);\n if (!rubric.ok) {\n return rubric;\n }\n // `rubricFacts` read every criterion, in order, or answered with a problem.\n const authored: readonly { measure?: unknown }[] = item.rubric.criteria;\n const judged: AiSpeakingCriterionFact[] = [];\n const measured: AiSpeakingMeasureFact[] = [];\n for (const [index, fact] of rubric.criteria.entries()) {\n const measure = (authored[index] as { measure?: unknown }).measure;\n if (measure !== undefined && !MEASURES.includes(measure)) {\n return { ok: false, problem: 'no-rubric', path: ['rubric', 'criteria', index, 'measure'] };\n }\n if (measure === undefined || input.judgeMeasures === true) {\n judged.push({\n ...fact,\n ...(measure !== undefined ? { measure: measure as SpeakingMeasure } : {}),\n });\n } else {\n measured.push({ name: fact.name, measure: measure as SpeakingMeasure, weight: fact.weight });\n }\n }\n if (judged.length === 0) {\n return { ok: false, problem: 'nothing-to-judge' };\n }\n const recording = response?.type === 'speaking-response' ? response.recording : null;\n if (recording === null || recording === undefined) {\n return { ok: false, problem: 'empty-response' };\n }\n const checked =\n input.assessment === null || input.assessment === undefined\n ? undefined\n : validateSpeechAssessment(input.assessment);\n if (checked === undefined || !checked.success) {\n return { ok: false, problem: 'invalid-assessment' };\n }\n const evidence = checked.data;\n if (evidence.status === 'no_speech') {\n return { ok: false, problem: 'empty-response' };\n }\n if (\n evidence.task !== 'unscripted' ||\n evidence.locale !== item.locale ||\n evidence.recordingKey !== recording.key\n ) {\n return { ok: false, problem: 'evidence-mismatch' };\n }\n if (evidence.transcript === undefined) {\n return { ok: false, problem: 'no-transcript' };\n }\n const text = evidence.transcript.text;\n const scores = evidence.scores;\n const scoreOf = {\n pronunciation: scores.accuracy,\n fluency: scores.fluency,\n prosody: scores.prosody,\n };\n const media =\n isRecord(item.media) && typeof item.media.type === 'string' ? item.media : undefined;\n const material = materialFacts(input.group);\n return {\n ok: true,\n request: {\n feature: 'speaking-grading',\n facts: {\n activityType: 'speaking-response',\n activityId: data.id,\n title: data.title,\n locale: item.locale,\n ...(typeof item.instructions === 'string' ? { instructions: item.instructions } : {}),\n prompt: typeof item.prompt === 'string' ? item.prompt : '',\n ...(media !== undefined\n ? {\n media: {\n type: media.type,\n ...(typeof media.alt === 'string' ? { alt: media.alt } : {}),\n },\n }\n : {}),\n text,\n wordCount: countWords(text),\n rubric: judged,\n measured: measured.map((fact) => {\n const score = scoreOf[fact.measure];\n return score !== undefined ? { ...fact, score } : fact;\n }),\n ...(typeof item.languageTarget === 'string' ? { languageTarget: item.languageTarget } : {}),\n },\n ...(material !== null ? { material } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n },\n };\n}\n\n/** Why a rubric cannot be given to a model, for either kind of answer. */\ntype RubricProblem = 'no-rubric' | 'duplicate-criteria' | 'unreadable-levels';\n\n/** The rubric as a model is given it, each criterion's levels read. */\nfunction rubricFacts(\n rubric: unknown,\n):\n | { ok: true; criteria: AiGradingCriterionFact[] }\n | { ok: false; problem: RubricProblem; path?: (string | number)[] } {\n const listed = isRecord(rubric) ? rubric.criteria : undefined;\n if (!Array.isArray(listed) || listed.length === 0) {\n return { ok: false, problem: 'no-rubric' };\n }\n const criteria: AiGradingCriterionFact[] = [];\n const names = new Set<string>();\n for (const [index, criterion] of listed.entries()) {\n if (!isRecord(criterion)) {\n return { ok: false, problem: 'no-rubric', path: ['rubric', 'criteria', index] };\n }\n const { name, description, weight } = criterion;\n if (typeof name !== 'string' || name.trim() === '' || typeof weight !== 'number') {\n return { ok: false, problem: 'no-rubric', path: ['rubric', 'criteria', index] };\n }\n // A model cannot tell two criteria named alike apart, so neither can a check.\n if (names.has(levelKey(name))) {\n return {\n ok: false,\n problem: 'duplicate-criteria',\n path: ['rubric', 'criteria', index, 'name'],\n };\n }\n names.add(levelKey(name));\n const levels = readRubricLevels(criterion.levels);\n if (!levels.ok) {\n return {\n ok: false,\n problem: 'unreadable-levels',\n path: ['rubric', 'criteria', index, 'levels'],\n };\n }\n criteria.push({\n name,\n ...(typeof description === 'string' ? { description } : {}),\n weight,\n levels: levels.levels,\n });\n }\n return { ok: true, criteria };\n}\n\n// ── Checking what comes back ───────────────────────────────────────────\n\ntype Refused = { ok: false; refusal: AiRefusal };\n\n/**\n * The rubric's criteria by name, folded as level labels are: composed, trimmed,\n * spaced once, lower-cased. A model that writes \"task achievement\" for \"Task\n * achievement\" judged that criterion. The request refused a rubric with two\n * names alike once folded, so a folded name is one criterion's alone.\n */\nfunction criterionIndex(\n rubric: readonly AiGradingCriterionFact[],\n): (name: string) => number | undefined {\n const folded = new Map(rubric.map((criterion, index) => [levelKey(criterion.name), index]));\n return (name) => folded.get(levelKey(name));\n}\n\n/** A finite number. */\nconst isNumber = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value);\n\n/**\n * One criterion's judgement, on that criterion's scale: a level it has, with\n * any probabilities a distribution over its levels whose likeliest is the\n * level chosen; or, without levels, a score from 0 to its `maxScore` (1 by\n * default).\n */\nfunction judgeCriterion(\n entry: Record<string, unknown>,\n criterion: AiGradingCriterionFact,\n): { ok: true; judged: Omit<AiGradedCriterion, 'comment'> } | Refused {\n const { level, score, maxScore, probabilities } = entry;\n const levels = criterion.levels;\n if (levels === null) {\n if (level !== undefined || probabilities !== undefined) {\n return { ok: false, refusal: 'out-of-scale' };\n }\n if (score === undefined) {\n return { ok: false, refusal: 'misses-criteria' };\n }\n if (!isNumber(score) || (maxScore !== undefined && !isNumber(maxScore))) {\n return { ok: false, refusal: 'malformed' };\n }\n const max = maxScore ?? 1;\n if (!(max > 0) || score < 0 || score - max > max * 1e-9) {\n return { ok: false, refusal: 'out-of-scale' };\n }\n return { ok: true, judged: { name: criterion.name, score: Math.min(1, score / max) } };\n }\n if (score !== undefined || maxScore !== undefined) {\n return { ok: false, refusal: 'out-of-scale' };\n }\n if (level === undefined) {\n return { ok: false, refusal: 'misses-criteria' };\n }\n if (typeof level !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n const chosen = levels.findIndex((one) => levelKey(one.label) === levelKey(level));\n if (chosen === -1) {\n return { ok: false, refusal: 'out-of-scale' };\n }\n const judged: Omit<AiGradedCriterion, 'comment'> = {\n name: criterion.name,\n level: (levels[chosen] as { label: string }).label,\n score: chosen / (levels.length - 1),\n };\n if (probabilities === undefined) {\n return { ok: true, judged };\n }\n if (!Array.isArray(probabilities)) {\n return { ok: false, refusal: 'malformed' };\n }\n const values = levelDistribution(probabilities, levels.length, chosen);\n if (values === null) {\n return { ok: false, refusal: 'out-of-scale' };\n }\n return { ok: true, judged: { ...judged, probabilities: values } };\n}\n\n/**\n * Checks a grade a port returned for `request`. Whole or nothing, as every AI\n * text is: a reply the SDK doubts in any part is refused, and is no grade —\n * re-ask the model or send the response to a person; never record a 0.\n *\n * - **Every criterion of the rubric, once** (`misses-criteria`). Names match as\n * written, or folded when the folded name is one criterion's alone. Leaving\n * one out, inventing or renaming one, judging one twice or calling one not\n * applicable refuses the reply: a total renormalised over fewer criteria\n * is a grade of a different task.\n * - **On each criterion's own scale** (`out-of-scale`). With levels: a `level`\n * the criterion has, as labelled (case and spacing aside), and any\n * `probabilities` a distribution over its levels, lowest first, whose\n * likeliest is the level chosen. Without levels: a `score` from 0 to its\n * `maxScore`, 1 by default.\n * - **Every correction quotes the response** (`misquotes-answer`), anchored as\n * feedback on writing anchors it.\n * - **Text fields** — a criterion's `comment` (at most\n * `AI_WRITING_MAX_FIELD_LENGTH` code points), `feedback` and `rationale`\n * (at most `AI_TEXT_MAX_LENGTH`) — are plain text, cleaned; longer is\n * `too-long`. `requiresHumanReview` is a boolean.\n *\n * The total is not computed here: `gradeFromJudgement` computes it, as\n * `gradeFromRubric` computes every grade in the SDK.\n */\nexport function checkAiGrading(\n raw: unknown,\n request: AiGradingRequest | AiSpeakingGradingRequest,\n): { ok: true; judgement: AiGradingJudgement } | Refused {\n if (!isRecord(raw)) {\n return { ok: false, refusal: 'malformed' };\n }\n const { criteria: listed, feedback, corrections, rationale, requiresHumanReview } = raw;\n if (listed === undefined) {\n return { ok: false, refusal: 'misses-criteria' };\n }\n if (!Array.isArray(listed)) {\n return { ok: false, refusal: 'malformed' };\n }\n const rubric = request.facts.rubric;\n const indexOf = criterionIndex(rubric);\n const judged: (AiGradedCriterion | undefined)[] = rubric.map(() => undefined);\n for (const entry of listed) {\n if (!isRecord(entry)) {\n return { ok: false, refusal: 'malformed' };\n }\n // Each field read once, by name: a getter must not pass a check with one\n // value and hand on another. Not rest syntax, which a transpiler may lower\n // to a copy that reads the excluded fields again.\n const name = entry.name;\n const notApplicable = entry.notApplicable;\n const written = entry.comment;\n const scale = {\n level: entry.level,\n score: entry.score,\n maxScore: entry.maxScore,\n probabilities: entry.probabilities,\n };\n if (\n typeof name !== 'string' ||\n (notApplicable !== undefined && typeof notApplicable !== 'boolean')\n ) {\n return { ok: false, refusal: 'malformed' };\n }\n const index = indexOf(name);\n if (index === undefined || judged[index] !== undefined || notApplicable === true) {\n return { ok: false, refusal: 'misses-criteria' };\n }\n const criterion = rubric[index] as AiGradingCriterionFact;\n const scaled = judgeCriterion(scale, criterion);\n if (!scaled.ok) {\n return scaled;\n }\n const comment = textField(written, AI_WRITING_MAX_FIELD_LENGTH);\n if (!comment.ok) {\n return comment;\n }\n judged[index] = {\n ...scaled.judged,\n ...(comment.value !== undefined ? { comment: comment.value } : {}),\n };\n }\n if (judged.some((one) => one === undefined)) {\n return { ok: false, refusal: 'misses-criteria' };\n }\n const anchored = checkCorrections(corrections, request.facts.text);\n if (!anchored.ok) {\n return anchored;\n }\n const said = textField(feedback, AI_TEXT_MAX_LENGTH);\n if (!said.ok) {\n return said;\n }\n const why = textField(rationale, AI_TEXT_MAX_LENGTH);\n if (!why.ok) {\n return why;\n }\n if (requiresHumanReview !== undefined && typeof requiresHumanReview !== 'boolean') {\n return { ok: false, refusal: 'malformed' };\n }\n const provenance = provenanceOf(raw.provenance);\n const usage = usageOf(raw.usage);\n return {\n ok: true,\n judgement: {\n criteria: judged as AiGradedCriterion[],\n feedback: said.value ?? null,\n corrections: anchored.corrections,\n ...(why.value !== undefined ? { rationale: why.value } : {}),\n requiresHumanReview: requiresHumanReview === true,\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n","import { AI_MATERIAL_MAX_LENGTH } from './ai-material.js';\nimport { provenanceOf, usageOf } from './ai-text.js';\nimport { keyPositionFinding } from './authoring/critique.js';\nimport {\n critiqueDraft,\n critiqueItemGroupDraft,\n validateDraft,\n validateItemGroupDraft,\n} from './authoring/index.js';\nimport { paperFindings } from './authoring/paper.js';\nimport { transcriptOf } from './media-refs.js';\nimport { getActivityTypeDescriptor } from './registry/index.js';\nimport { PLACEHOLDER_RE } from './schemas/fill-in-the-blanks.js';\nimport { seededShuffle } from './shuffle.js';\nimport type { ActivityType } from './types/activity.js';\nimport type {\n AiDraftProblem,\n AiDraftSettings,\n AiDraftsCall,\n AiGeneratedVariant,\n AiRefusal,\n AiVariantDifficulty,\n AiVariantExemplarFacts,\n AiVariantImage,\n AiVariantLevelFacts,\n AiVariantMismatch,\n AiVariantMissing,\n AiVariantPair,\n AiVariantSkip,\n AiVariantSkipCode,\n AiVariants,\n AiVariantsRequest,\n AiVariantsRun,\n AiVariantTargets,\n} from './types/ai.js';\nimport type { ItemFinding } from './types/authoring.js';\nimport {\n AI_VARIANT_NEAR_COPY_THRESHOLD,\n inPassageOrder,\n keysOf,\n type LengthTolerance,\n lengthMisses,\n lengthTargets,\n optionSet,\n sharedAnswers,\n shownScore,\n structureMisses,\n structureTargets,\n textSimilarity,\n textsOf,\n} from './variant-rules.js';\n\nexport {\n AI_VARIANT_LENGTH_BANDS,\n AI_VARIANT_NEAR_COPY_THRESHOLD,\n type LengthTolerance,\n} from './variant-rules.js';\n\n/** The most exemplars one request may vary: a long section is sent in parts. */\nexport const AI_VARIANTS_MAX_EXEMPLARS = 20;\n\n/** The most repair passes `generateVariants` makes. */\nexport const AI_VARIANTS_MAX_REPAIRS = 3;\n\n/** The longest passage or script, in characters, an exemplar may carry into the facts. */\nexport const AI_VARIANTS_MAX_MATERIAL_LENGTH = AI_MATERIAL_MAX_LENGTH;\n\n/**\n * Every code a variant can be reported with, and its severity: a contract,\n * as the critic's codes are. `docs/ai.md` documents this table and a test\n * holds the two together.\n */\nexport const AI_VARIANT_CODE_SEVERITY = {\n variant_missing: 'invalid',\n variant_type: 'invalid',\n variant_structure: 'invalid',\n variant_length: 'warning',\n variant_near_copy: 'warning',\n variant_same_key: 'warning',\n} as const;\n\n/** The activity types the SDK writes variants of: the ones whose every field it knows. */\nconst VARIANT_TYPES: readonly string[] = [\n 'multiple-choice',\n 'fill-in-the-blanks',\n 'gap-select',\n 'dictation',\n 'read-aloud',\n 'written-response',\n] satisfies readonly ActivityType[];\n\nconst DIFFICULTIES: readonly string[] = ['easier', 'same', 'harder'];\n\ntype Fields = Record<string, unknown>;\n\nconst isRecord = (value: unknown): value is Fields =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\nconst listOf = (value: unknown): readonly unknown[] => (Array.isArray(value) ? value : []);\nconst stringOf = (value: unknown): string => (typeof value === 'string' ? value : '');\nconst written = (value: unknown): string | undefined =>\n typeof value === 'string' && value.trim() !== '' ? value : undefined;\nconst textList = (value: unknown): string[] =>\n listOf(value).filter((one): one is string => typeof one === 'string');\n/** A copy of plain content, so a variant never shares an object with its exemplar. */\nconst clone = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\nconst isRecording = (media: unknown): boolean =>\n isRecord(media) && ['audio', 'video', 'embed'].includes(stringOf(media.type));\nconst isImage = (media: unknown): boolean => isRecord(media) && media.type === 'image';\n\n// ── What a model is told: rules, and levers ───────────────────────────\n\nconst RULES = [\n 'Write one variant for each exemplar, naming its exemplar index and its type.',\n \"Keep each exemplar's targets.structure exactly: the same number of options and of right options, blanks, gaps, choices and banks, items in the same order, and speakers.\",\n \"Write every word a learner reads or hears, in new words: never the exemplar's sentences with a few words changed. Write nothing else: no ids, HTML, addresses or settings.\",\n 'Keep each text within its targets.lengths, counted in words.',\n \"Test the same skill with the same kind of thinking at the same level: a literal question stays literal, an inference stays an inference, and wrong options stay as plausible as the exemplar's.\",\n \"Give each item a right answer other than its exemplar's, and other than the answers in avoid, unless the skill tested leaves only one.\",\n \"Keep the task in instructions as the exemplar's; change its wording only where the new item needs it.\",\n 'Keep a title that is a label (Part 1) as it is; give a title that names the topic a new one that gives no answer away.',\n 'Do not repeat the flaws listed in targets.exemplarFindings.',\n 'A listening script keeps the number of speakers, with new names, and about as many turns.',\n];\n\nconst DESCRIBE_RULE =\n 'Where an exemplar shows a picture, describe a new picture in its media alt, specific enough for a person to find or draw it, and ask about that picture.';\nconst REUSE_RULE =\n 'Where an exemplar shows a picture, keep it: write new questions about the same picture, which its alt describes.';\nconst DIFFICULTY_RULE =\n 'Where targets.difficulty is easier or harder, move one step, inside the level, using targets.levers. Never write for another level.';\nconst LEVEL_FACTS_RULE =\n 'Use only grammar and vocabulary that levelFacts lists as taught, and keep away from the topics it lists.';\n\n/** The ways a type is made easier or harder: one step inside a level. */\nconst LEVERS: Readonly<Record<string, Readonly<Record<'easier' | 'harder', readonly string[]>>>> = {\n 'multiple-choice': {\n easier: [\n 'Make the wrong options clearly wrong to a learner who understood, though still plausible to one who did not.',\n 'Ask about what is said directly rather than what is implied.',\n 'Word the right option closer to the words of the text or recording.',\n ],\n harder: [\n 'Make the wrong options closer to the right one, each wrong for a reason the text or recording makes clear.',\n 'Ask for an inference rather than a fact stated directly.',\n 'Paraphrase the right option rather than repeat the words of the text or recording.',\n ],\n },\n 'fill-in-the-blanks': {\n easier: ['Give more context around each blank that points to the answer.'],\n harder: [\n 'Give less context around each blank, so the learner must know the form rather than infer it.',\n ],\n },\n 'gap-select': {\n easier: [\n 'Give more context around each gap that points to the answer.',\n 'Make the wrong choices clearly wrong in context.',\n ],\n harder: [\n 'Give less context around each gap.',\n 'Make the wrong choices closer to the right one.',\n ],\n },\n dictation: {\n easier: ['Use shorter sentences and more frequent words of the level, inside the length band.'],\n harder: [\n 'Use longer sentences and less frequent words of the level, inside the length band.',\n 'Use more connected-speech features: contractions, linking, weak forms.',\n ],\n },\n 'read-aloud': {\n easier: ['Use shorter sentences and more frequent words of the level, inside the length band.'],\n harder: [\n 'Use longer sentences and less frequent words of the level, inside the length band.',\n 'Use more words with sounds and stress the level finds hard.',\n ],\n },\n 'written-response': {\n easier: ['Make the task more specific, and supply more of the content to write about.'],\n harder: ['Make the task more open, so the learner supplies more of the content.'],\n },\n reading: {\n easier: [\n 'Ask about what the passage says directly rather than what it implies.',\n 'Use more frequent words of the level, and shorter sentences with simpler syntax.',\n ],\n harder: [\n 'Ask for inferences rather than facts stated directly.',\n 'Use less frequent words of the level, and longer sentences.',\n ],\n },\n listening: {\n easier: [\n 'Describe a slower pace in the script, and have a speaker repeat the key information.',\n 'Use more frequent words of the level.',\n ],\n harder: [\n 'Describe a natural pace in the script, and give the key information once.',\n 'Use less frequent words of the level.',\n ],\n },\n};\n\nfunction leversFor(entry: Fields, difficulty: AiVariantDifficulty): string[] | undefined {\n if (difficulty === 'same') {\n return undefined;\n }\n let kind = stringOf(entry.type);\n if (kind === 'item-group') {\n const stimulus = isRecord(entry.stimulus) ? entry.stimulus : {};\n kind = stimulus.kind === 'audio' || stimulus.kind === 'video' ? 'listening' : 'reading';\n }\n const levers = LEVERS[kind];\n return levers !== undefined ? [...levers[difficulty]] : undefined;\n}\n\n// ── The exemplar, as a model is shown it ──────────────────────────────\n\n/** A passage with its placeholders numbered in order: `{{1}}`, `{{2}}`… — how a variant writes them. */\nfunction renumbered(passage: string): string {\n let position = 0;\n return passage.replace(PLACEHOLDER_RE, () => {\n position += 1;\n return `{{${position}}}`;\n });\n}\n\nconst turnsOf = (holder: Fields): { speaker: string; text: string }[] =>\n listOf(holder.script).flatMap((turn) =>\n isRecord(turn) ? [{ speaker: stringOf(turn.speaker), text: stringOf(turn.text) }] : [],\n );\n\nconst hasScript = (holder: Fields): boolean => listOf(holder.script).some(isRecord);\n\n/** What a model is shown of a media object: a picture's description, or what a recording says. */\nfunction mediaContent(\n media: unknown,\n ownText: boolean,\n ref: string | undefined,\n): Fields | undefined {\n if (!isRecord(media)) {\n return undefined;\n }\n if (isImage(media)) {\n return { alt: stringOf(media.alt), ...(ref !== undefined ? { ref } : {}) };\n }\n if (ownText || !isRecording(media)) {\n return undefined;\n }\n if (hasScript(media)) {\n return { script: turnsOf(media) };\n }\n return typeof media.transcript === 'string' ? { transcript: media.transcript } : undefined;\n}\n\nfunction feedbackContent(holder: Fields): Fields {\n if (!isRecord(holder.feedback)) {\n return {};\n }\n const { correct, incorrect } = holder.feedback;\n return {\n feedback: {\n ...(typeof correct === 'string' ? { correct } : {}),\n ...(typeof incorrect === 'string' ? { incorrect } : {}),\n },\n };\n}\n\ntype RefOf = (path: readonly (string | number)[]) => string | undefined;\n\nfunction itemContent(item: Fields, refOf: RefOf, at: readonly (string | number)[]): Fields {\n const type = stringOf(item.type);\n const ownText = type === 'dictation' || type === 'read-aloud';\n const media = mediaContent(item.media, ownText, refOf([...at, 'media']));\n const content: Fields = {\n type,\n title: stringOf(item.title),\n ...(typeof item.instructions === 'string' ? { instructions: item.instructions } : {}),\n };\n switch (type) {\n case 'multiple-choice':\n Object.assign(content, {\n question: stringOf(item.question),\n options: listOf(item.options).map((option, index) => {\n const fields = isRecord(option) ? option : {};\n const optionMedia = mediaContent(\n fields.media,\n false,\n refOf([...at, 'options', index, 'media']),\n );\n return {\n text: stringOf(fields.text),\n isCorrect: fields.isCorrect === true,\n ...(typeof fields.feedback === 'string' ? { feedback: fields.feedback } : {}),\n ...(optionMedia !== undefined ? { media: optionMedia } : {}),\n };\n }),\n });\n break;\n case 'fill-in-the-blanks':\n Object.assign(content, {\n passage: renumbered(stringOf(item.passage)),\n blanks: inPassageOrder(item, 'blanks').map(({ value }) => {\n const blank = isRecord(value) ? value : {};\n return {\n acceptedAnswers: textList(blank.acceptedAnswers),\n ...(typeof blank.hint === 'string' ? { hint: blank.hint } : {}),\n ...(typeof blank.feedback === 'string' ? { feedback: blank.feedback } : {}),\n };\n }),\n });\n break;\n case 'gap-select': {\n const banks = listOf(item.banks).filter(isRecord);\n Object.assign(content, {\n passage: renumbered(stringOf(item.passage)),\n gaps: inPassageOrder(item, 'gaps').map(({ value }) => {\n const gap = isRecord(value) ? value : {};\n const feedback = typeof gap.feedback === 'string' ? { feedback: gap.feedback } : {};\n const bank = banks.find((one) => one.id === gap.bankId);\n if (listOf(gap.choices).length === 0 && bank !== undefined) {\n const right = listOf(bank.choices).find(\n (choice) => isRecord(choice) && choice.id === gap.correctChoiceId,\n );\n return { answer: isRecord(right) ? stringOf(right.text) : '', ...feedback };\n }\n return {\n choices: listOf(gap.choices).map((choice) => ({\n text: isRecord(choice) ? stringOf(choice.text) : '',\n isCorrect: isRecord(choice) && choice.id === gap.correctChoiceId,\n })),\n ...feedback,\n };\n }),\n ...(banks.length > 0\n ? {\n banks: banks.map((bank) => ({\n choices: listOf(bank.choices).map((choice) =>\n isRecord(choice) ? stringOf(choice.text) : '',\n ),\n })),\n }\n : {}),\n });\n break;\n }\n case 'dictation':\n Object.assign(content, {\n transcript: stringOf(item.transcript),\n ...(Array.isArray(item.acceptedTranscripts)\n ? { acceptedTranscripts: textList(item.acceptedTranscripts) }\n : {}),\n });\n break;\n case 'read-aloud':\n content.referenceText = stringOf(item.referenceText);\n break;\n default:\n content.prompt = stringOf(item.prompt);\n }\n return {\n ...content,\n ...(media !== undefined ? { media } : {}),\n ...feedbackContent(item),\n };\n}\n\nfunction entryContent(entry: Fields, refOf: RefOf): Fields {\n if (entry.type !== 'item-group') {\n return itemContent(entry, refOf, []);\n }\n const stimulus = isRecord(entry.stimulus) ? entry.stimulus : {};\n const ownText = hasScript(stimulus) || typeof stimulus.transcript === 'string';\n const media = mediaContent(stimulus.media, ownText, refOf(['stimulus', 'media']));\n return {\n type: 'item-group',\n ...(typeof entry.title === 'string' ? { title: entry.title } : {}),\n stimulus: {\n kind: stringOf(stimulus.kind),\n ...(typeof stimulus.title === 'string' ? { title: stimulus.title } : {}),\n ...(typeof stimulus.body === 'string' ? { body: stimulus.body } : {}),\n ...(hasScript(stimulus)\n ? { script: turnsOf(stimulus) }\n : typeof stimulus.transcript === 'string'\n ? { transcript: stimulus.transcript }\n : {}),\n ...(media ?? {}),\n },\n items: listOf(entry.items).map((item, index) =>\n isRecord(item) ? itemContent(item, refOf, ['items', index]) : {},\n ),\n };\n}\n\n/** Every picture an entry shows: an item's, an option's, and a group's material's. */\nfunction imageSlots(entry: Fields): { path: string[]; media: Fields }[] {\n const ofItem = (item: unknown, at: string[]): { path: string[]; media: Fields }[] => {\n if (!isRecord(item)) {\n return [];\n }\n return [\n ...(isImage(item.media) ? [{ path: [...at, 'media'], media: item.media as Fields }] : []),\n ...listOf(item.options).flatMap((option, index) =>\n isRecord(option) && isImage(option.media)\n ? [{ path: [...at, 'options', String(index), 'media'], media: option.media as Fields }]\n : [],\n ),\n ];\n };\n if (entry.type !== 'item-group') {\n return ofItem(entry, []);\n }\n const stimulus = isRecord(entry.stimulus) ? entry.stimulus : {};\n return [\n ...(isImage(stimulus.media)\n ? [{ path: ['stimulus', 'media'], media: stimulus.media as Fields }]\n : []),\n ...listOf(entry.items).flatMap((item, index) => ofItem(item, ['items', String(index)])),\n ];\n}\n\n// ── Exemplars that cannot be varied ────────────────────────────────────\n\n/** Whether a recording the item's meaning depends on says nothing the facts can carry. */\nfunction unheard(entry: Fields): boolean {\n const itemUnheard = (item: unknown): boolean => {\n if (!isRecord(item) || item.type === 'dictation' || item.type === 'read-aloud') {\n return false;\n }\n return (\n (isRecording(item.media) && transcriptOf(item.media) === null) ||\n listOf(item.options).some(\n (option) =>\n isRecord(option) && isRecording(option.media) && transcriptOf(option.media) === null,\n )\n );\n };\n if (entry.type !== 'item-group') {\n return itemUnheard(entry);\n }\n const stimulus = isRecord(entry.stimulus) ? entry.stimulus : {};\n const listening =\n stimulus.kind === 'audio' || stimulus.kind === 'video' || isRecording(stimulus.media);\n return (listening && transcriptOf(stimulus) === null) || listOf(entry.items).some(itemUnheard);\n}\n\nfunction skipOf(\n entry: Fields,\n exemplarIndex: number,\n imagePolicy: 'describe' | 'reuse',\n): AiVariantSkip | undefined {\n const skip = (code: AiVariantSkipCode, message: string): AiVariantSkip => ({\n exemplarIndex,\n code,\n message,\n });\n const group = entry.type === 'item-group';\n if (group && entry.timeline !== undefined && entry.timeline !== null) {\n return skip(\n 'variant_exemplar_timeline',\n 'An interactive video is not varied: its questions are timed to its video.',\n );\n }\n const types = group\n ? listOf(entry.items).map((item) => (isRecord(item) ? stringOf(item.type) : ''))\n : [stringOf(entry.type)];\n const unsupported = types.find(\n (type) => getActivityTypeDescriptor(type) !== undefined && !VARIANT_TYPES.includes(type),\n );\n if (unsupported !== undefined) {\n return skip(\n 'variant_exemplar_unsupported',\n `The SDK does not write variants of ${unsupported}: it does not know which of its fields are words and which are settings.`,\n );\n }\n if (\n imagePolicy === 'reuse' &&\n imageSlots(entry).some((slot) => written(slot.media.alt) === undefined)\n ) {\n return skip(\n 'variant_exemplar_unseen',\n 'A picture this depends on has no description (alt), so a model cannot write questions about it.',\n );\n }\n const validation = group\n ? validateItemGroupDraft(entry)\n : validateDraft(entry.type as ActivityType, entry);\n if (validation.status !== 'complete') {\n const first = validation.issues[0];\n return skip(\n 'variant_exemplar_incomplete',\n `This is not a finished item (${first !== undefined ? `${first.code} at ${first.path.join('.') || 'its root'}` : validation.status}), so it has no structure to copy.`,\n );\n }\n if (unheard(entry)) {\n return skip(\n 'variant_exemplar_unheard',\n 'A recording this depends on has no transcript or script, so a model cannot know what it says.',\n );\n }\n const long = textsOf(entry).find((text) => text.text.length > AI_VARIANTS_MAX_MATERIAL_LENGTH);\n if (long !== undefined) {\n return skip(\n 'variant_exemplar_too_long',\n `${long.path.join('.')} is longer than ${AI_VARIANTS_MAX_MATERIAL_LENGTH} characters. Send a shorter part.`,\n );\n }\n return undefined;\n}\n\n// ── The shape a model writes ───────────────────────────────────────────\n\nconst text = (description: string) => ({ type: 'string', description });\n\nconst SCRIPT_SHAPE = {\n type: 'array',\n minItems: 1,\n description:\n 'The recording as turns, in order: a new name for each speaker, as many speakers as the exemplar.',\n items: {\n type: 'object',\n required: ['speaker', 'text'],\n properties: { speaker: text('Who speaks.'), text: text('What they say.') },\n },\n};\n\nconst MEDIA_SHAPE = {\n type: 'object',\n description:\n \"Only where the exemplar's content has media: for a picture, its alt; for a recording, its script — or transcript where the exemplar has no script.\",\n properties: {\n alt: text('What the picture shows.'),\n transcript: text('What the recording says.'),\n script: SCRIPT_SHAPE,\n },\n};\n\nconst FEEDBACK_SHAPE = {\n type: 'object',\n description: 'Only where the exemplar has feedback: shown after answering.',\n properties: {\n correct: text('Shown when the learner passes.'),\n incorrect: text('Shown when the learner does not.'),\n },\n};\n\nconst FIELD_SHAPES: Readonly<Record<string, Fields>> = {\n title: text('A short name for the item, shown above it. Never the answer, or any part of it.'),\n instructions: text(\n 'Only where the exemplar has instructions: the same task, worded for the new item.',\n ),\n question: text('The question, as plain text.'),\n options: {\n type: 'array',\n description: \"As many as the exemplar's, as many of them right.\",\n items: {\n type: 'object',\n required: ['text', 'isCorrect'],\n properties: {\n text: text('The option as the learner reads it.'),\n isCorrect: { type: 'boolean' },\n feedback: text(\"Only where the exemplar's options have feedback.\"),\n media: MEDIA_SHAPE,\n },\n },\n },\n passage: text('The passage as plain text, with {{1}}, {{2}}, … where each gap goes, in order.'),\n blanks: {\n type: 'array',\n description: 'One per gap, in the order the gaps appear: the first is {{1}}.',\n items: {\n type: 'object',\n required: ['acceptedAnswers'],\n properties: {\n acceptedAnswers: {\n type: 'array',\n minItems: 1,\n items: { type: 'string' },\n description: 'Every answer that is right, spelled as a learner would type it.',\n },\n hint: text(\"Only where the exemplar's blank has a hint. Never gives the answer away.\"),\n feedback: text(\"Only where the exemplar's blank has feedback.\"),\n },\n },\n },\n gaps: {\n type: 'array',\n description:\n 'One per gap, in the order the gaps appear. A gap with its own choices writes `choices`, exactly one right; a gap drawing on a bank writes `answer`, the text of its right choice in that bank.',\n items: {\n type: 'object',\n properties: {\n choices: {\n type: 'array',\n items: {\n type: 'object',\n required: ['text', 'isCorrect'],\n properties: { text: text('A choice.'), isCorrect: { type: 'boolean' } },\n },\n },\n answer: text(\"The right choice's text, exactly as its bank writes it.\"),\n feedback: text(\"Only where the exemplar's gap has feedback.\"),\n },\n },\n },\n banks: {\n type: 'array',\n description: \"The shared choice lists, as many as the exemplar's, each with as many choices.\",\n items: {\n type: 'object',\n required: ['choices'],\n properties: { choices: { type: 'array', items: { type: 'string' } } },\n },\n },\n transcript: text('What the learner hears and types.'),\n acceptedTranscripts: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Only where the exemplar has them: other writings of the transcript that are right, of the same kind.',\n },\n referenceText: text('What the learner reads aloud.'),\n prompt: text(\n \"The writing task, of the same genre and purpose as the exemplar's, about something new.\",\n ),\n media: MEDIA_SHAPE,\n feedback: FEEDBACK_SHAPE,\n};\n\nconst WRITES: Readonly<Record<string, readonly string[]>> = {\n 'multiple-choice': ['title', 'instructions', 'question', 'options', 'media', 'feedback'],\n 'fill-in-the-blanks': ['title', 'instructions', 'passage', 'blanks', 'media', 'feedback'],\n 'gap-select': ['title', 'instructions', 'passage', 'gaps', 'banks', 'media', 'feedback'],\n dictation: ['title', 'transcript', 'acceptedTranscripts', 'feedback'],\n 'read-aloud': ['title', 'instructions', 'referenceText', 'feedback'],\n 'written-response': ['title', 'prompt', 'media', 'feedback'],\n};\n\nfunction itemProperties(types: readonly string[]): Fields {\n const properties: Fields = {};\n for (const type of types) {\n for (const name of WRITES[type] ?? []) {\n properties[name] = FIELD_SHAPES[name];\n }\n }\n return properties;\n}\n\n/** The types an entry holds, a group's items included. */\nconst typesIn = (entry: Fields): string[] =>\n entry.type === 'item-group'\n ? listOf(entry.items).map((item) => (isRecord(item) ? stringOf(item.type) : ''))\n : [stringOf(entry.type)];\n\nfunction replyShape(entries: readonly Fields[], exactly: number): Fields {\n const top = [...new Set(entries.map((entry) => stringOf(entry.type)))];\n const itemTypes = [\n ...new Set(entries.filter((entry) => entry.type === 'item-group').flatMap(typesIn)),\n ].filter((type) => VARIANT_TYPES.includes(type));\n const properties: Fields = {\n exemplar: {\n type: 'integer',\n minimum: 0,\n description: \"The exemplar's index, as the facts give it.\",\n },\n type: { type: 'string', enum: top, description: \"The exemplar's type.\" },\n ...itemProperties(top.filter((type) => type !== 'item-group')),\n };\n if (top.includes('item-group')) {\n properties.title = FIELD_SHAPES.title;\n properties.stimulus = {\n type: 'object',\n description: 'The new material: a passage, or what a recording says.',\n properties: {\n title: text(\"Only where the exemplar's material has a title.\"),\n body: text('The passage, as plain text.'),\n transcript: text('What the recording says, where the exemplar has no script.'),\n script: SCRIPT_SHAPE,\n alt: text('For a picture: what it shows.'),\n },\n };\n properties.items = {\n type: 'array',\n description: \"The questions about the new material: the exemplar's types, in its order.\",\n items: {\n type: 'object',\n required: ['type'],\n properties: { type: { type: 'string', enum: itemTypes }, ...itemProperties(itemTypes) },\n },\n };\n }\n return {\n type: 'object',\n required: ['variants'],\n properties: {\n variants: {\n type: 'array',\n minItems: exactly,\n maxItems: exactly,\n items: { type: 'object', required: ['exemplar', 'type'], properties },\n },\n },\n };\n}\n\n// ── The request ────────────────────────────────────────────────────────\n\nconst facing = (path: readonly string[]): string =>\n path.join('.').replace(/^stimulus\\.media\\./, 'stimulus.');\n\nfunction targetsOf(\n entry: Fields,\n difficulty: AiVariantDifficulty,\n reason: string | undefined,\n itemDifficulty: AiVariantTargets['itemDifficulty'],\n tolerance: LengthTolerance | undefined,\n): AiVariantTargets {\n const group = entry.type === 'item-group';\n const levers = leversFor(entry, difficulty);\n const findings = group\n ? critiqueItemGroupDraft(entry)\n : critiqueDraft(entry.type as ActivityType, entry);\n return {\n type: stringOf(entry.type),\n structure: structureTargets(entry),\n lengths: Object.fromEntries(\n lengthTargets(entry, tolerance).map((target) => [facing(target.path), target.bounds]),\n ),\n difficulty,\n ...(difficulty !== 'same' && reason !== undefined ? { difficultyReason: reason } : {}),\n ...(levers !== undefined ? { levers } : {}),\n ...(itemDifficulty !== undefined ? { itemDifficulty } : {}),\n exemplarFindings: findings.map((found) => ({ code: found.code, message: found.message })),\n };\n}\n\nconst idOf = (value: unknown): string | undefined =>\n isRecord(value) && typeof value.id === 'string' ? value.id : undefined;\n\n/**\n * What a model is given to write a variant of each exemplar — a parallel item:\n * the exemplar's type, structure, scoring and settings, with every word a\n * learner reads or hears new — and what `checkAiVariants` needs to check the\n * reply against.\n *\n * `exemplars` are loose activities and item groups as stored, answer keys and\n * author-only transcripts included, in paper order; one variant is written for\n * each that can be varied. An exemplar that cannot is left out of the facts\n * and listed in `skipped`: a recording with nothing to say what it says\n * (`variant_exemplar_unheard`), a picture with no description to reuse\n * (`variant_exemplar_unseen`), an unfinished item\n * (`variant_exemplar_incomplete`), an interactive video\n * (`variant_exemplar_timeline`), a type the SDK does not vary\n * (`variant_exemplar_unsupported`) or material over\n * {@link AI_VARIANTS_MAX_MATERIAL_LENGTH} characters\n * (`variant_exemplar_too_long`). When every exemplar is skipped the request has\n * none, and `generateVariants` makes no call.\n *\n * The facts give each exemplar's content in the shape a variant is written in,\n * and its `targets`: the structure to keep, the length of each text in words,\n * its difficulty with the levers to move it, and the critic's findings on it\n * not to repeat. `rules` are what every variant must respect, as sentences a\n * prompt can quote. Pictures are listed in `images`, for a host that attaches\n * them; no recording's address reaches the facts. `avoid` is the other forms'\n * items: compared with every variant, never varied.\n *\n * Nothing passed is changed.\n *\n * @throws RangeError when `exemplars` is empty or longer than\n * {@link AI_VARIANTS_MAX_EXEMPLARS}, an exemplar is neither an item group nor\n * a registered activity type, `level` is empty, a difficulty is not `easier`,\n * `same` or `harder`, `images` is neither `describe` nor `reuse`, or\n * `difficultyFor` names an id no exemplar has.\n */\nexport function aiVariantsRequest(input: {\n exemplars: readonly unknown[];\n level: string;\n difficulty?: AiVariantDifficulty;\n difficultyFor?: Readonly<Record<string, { difficulty: AiVariantDifficulty; reason?: string }>>;\n locale?: string;\n levelFacts?: AiVariantLevelFacts;\n avoid?: readonly unknown[];\n images?: 'describe' | 'reuse';\n instructions?: string;\n settings?: AiDraftSettings;\n lengthTolerance?: LengthTolerance;\n balanceKeyPositions?: { seed: string };\n}): AiVariantsRequest {\n const { exemplars, level, locale, levelFacts, avoid, instructions, settings } = input;\n const difficulty = input.difficulty ?? 'same';\n const imagePolicy = input.images ?? 'describe';\n if (\n !Array.isArray(exemplars) ||\n exemplars.length === 0 ||\n exemplars.length > AI_VARIANTS_MAX_EXEMPLARS\n ) {\n throw new RangeError(\n `aiVariantsRequest: exemplars must hold 1 to ${AI_VARIANTS_MAX_EXEMPLARS} entries; send a longer section in parts.`,\n );\n }\n if (typeof level !== 'string' || level.trim() === '') {\n throw new RangeError('aiVariantsRequest: level is required: a variant must stay inside it.');\n }\n if (!DIFFICULTIES.includes(difficulty)) {\n throw new RangeError(\n `aiVariantsRequest: difficulty must be easier, same or harder, not ${String(difficulty)}.`,\n );\n }\n if (imagePolicy !== 'describe' && imagePolicy !== 'reuse') {\n throw new RangeError(\n `aiVariantsRequest: images must be describe or reuse, not ${String(imagePolicy)}.`,\n );\n }\n exemplars.forEach((entry, index) => {\n const type = isRecord(entry) ? entry.type : undefined;\n if (\n typeof type !== 'string' ||\n (type !== 'item-group' && getActivityTypeDescriptor(type) === undefined)\n ) {\n throw new RangeError(\n `aiVariantsRequest: exemplar ${index} is neither an item group nor a registered activity type.`,\n );\n }\n });\n const entries = exemplars as readonly Fields[];\n const ids = new Set(\n entries\n .flatMap((entry) => [idOf(entry), ...listOf(entry.items).map(idOf)])\n .filter((id): id is string => id !== undefined),\n );\n for (const [id, wanted] of Object.entries(input.difficultyFor ?? {})) {\n if (!ids.has(id)) {\n throw new RangeError(\n `aiVariantsRequest: difficultyFor names \"${id}\", which no exemplar has.`,\n );\n }\n if (!isRecord(wanted) || !DIFFICULTIES.includes(wanted.difficulty as string)) {\n throw new RangeError(\n `aiVariantsRequest: difficultyFor[\"${id}\"] must be easier, same or harder.`,\n );\n }\n }\n const moved = (id: string | undefined) =>\n id !== undefined ? input.difficultyFor?.[id] : undefined;\n\n const skipped: AiVariantSkip[] = [];\n const images: AiVariantImage[] = [];\n const facts: AiVariantExemplarFacts[] = [];\n entries.forEach((entry, exemplarIndex) => {\n const skip = skipOf(entry, exemplarIndex, imagePolicy);\n if (skip !== undefined) {\n skipped.push(skip);\n return;\n }\n const refs = new Map<string, string>();\n for (const slot of imageSlots(entry)) {\n const ref = `img-${images.length + 1}`;\n refs.set(slot.path.join('.'), ref);\n images.push({\n ref,\n exemplarIndex,\n path: slot.path,\n ...(typeof slot.media.alt === 'string' ? { alt: slot.media.alt } : {}),\n url: stringOf(slot.media.url),\n });\n }\n const own = moved(idOf(entry));\n const entryDifficulty = own?.difficulty ?? difficulty;\n const items = entry.type === 'item-group' ? listOf(entry.items) : [];\n const perItem = items.map((item, at) => {\n const wanted = moved(idOf(item));\n return {\n item: at,\n difficulty: wanted?.difficulty ?? entryDifficulty,\n ...(wanted?.reason !== undefined ? { difficultyReason: wanted.reason } : {}),\n };\n });\n const itemDifficulty = perItem.some(\n (one) => one.difficulty !== entryDifficulty || one.difficultyReason !== undefined,\n )\n ? perItem.map((one) => {\n const levers = leversFor(items[one.item] as Fields, one.difficulty);\n return { ...one, ...(levers !== undefined ? { levers } : {}) };\n })\n : undefined;\n facts.push({\n exemplar: exemplarIndex,\n type: stringOf(entry.type),\n content: entryContent(entry, (path) => refs.get(path.map(String).join('.'))),\n ...(Array.isArray(entry.learningObjectives)\n ? { learningObjectives: textList(entry.learningObjectives) }\n : {}),\n targets: targetsOf(\n entry,\n entryDifficulty,\n own?.reason,\n itemDifficulty,\n input.lengthTolerance,\n ),\n });\n });\n\n const rules = [\n ...RULES,\n ...(images.length > 0 ? [imagePolicy === 'describe' ? DESCRIBE_RULE : REUSE_RULE] : []),\n ...(facts.some(\n (one) =>\n one.targets.difficulty !== 'same' ||\n (one.targets.itemDifficulty ?? []).some((item) => item.difficulty !== 'same'),\n )\n ? [DIFFICULTY_RULE]\n : []),\n ...(levelFacts !== undefined ? [LEVEL_FACTS_RULE] : []),\n ];\n const varied = facts.map((one) => entries[one.exemplar] as Fields);\n return {\n feature: 'variant-generation',\n facts: {\n exemplars: facts,\n level,\n ...(locale !== undefined\n ? { locale }\n : typeof entries[0]?.locale === 'string'\n ? { locale: entries[0].locale }\n : {}),\n difficulty,\n ...(levelFacts !== undefined ? { levelFacts: clone(levelFacts) } : {}),\n imagePolicy,\n ...(images.length > 0 ? { images } : {}),\n ...(avoid !== undefined && avoid.length > 0\n ? {\n avoid: avoid.map((entry) => ({\n texts: textsOf(entry).map((one) => one.text),\n keys: (isRecord(entry) && entry.type === 'item-group'\n ? listOf(entry.items)\n : [entry]\n ).flatMap((item) => keysOf(item).map((slot) => slot.shown)),\n })),\n }\n : {}),\n ...(instructions !== undefined ? { instructions } : {}),\n rules,\n },\n shape: replyShape(varied, varied.length),\n exemplars,\n ...(avoid !== undefined ? { avoid } : {}),\n ...(settings !== undefined ? { settings } : {}),\n ...(input.lengthTolerance !== undefined\n ? { lengthTolerance: { ...input.lengthTolerance } }\n : {}),\n ...(input.balanceKeyPositions !== undefined\n ? { balanceKeyPositions: { seed: input.balanceKeyPositions.seed } }\n : {}),\n skipped,\n };\n}\n\n// ── From a reply to a variant ──────────────────────────────────────────\n\n/** Fields every activity copies from its exemplar: what scores it, and what it tests. */\nconst COPIED_COMMON = [\n 'scoringStrategy',\n 'shuffle',\n 'passThreshold',\n 'locale',\n 'ai',\n 'learningObjectives',\n 'slotKey',\n];\n\nconst COPIED: Readonly<Record<string, readonly string[]>> = {\n 'multiple-choice': ['mode'],\n 'fill-in-the-blanks': [],\n 'gap-select': ['presentation', 'shuffleChoices'],\n dictation: ['tolerance', 'hints'],\n 'read-aloud': ['recording', 'scoring'],\n 'written-response': ['rubric', 'minWords', 'maxWords', 'languageTarget'],\n};\n\nfunction pick(from: Fields, names: readonly string[]): Fields {\n const out: Fields = {};\n for (const name of names) {\n if (from[name] !== undefined) {\n out[name] = clone(from[name]);\n }\n }\n return out;\n}\n\n/** The author's 1–5 difficulty, one step along with the variant's, inside the scale. */\nfunction steppedLevel(level: unknown, difficulty: AiVariantDifficulty): Fields {\n if (typeof level !== 'number') {\n return {};\n }\n const step = difficulty === 'easier' ? -1 : difficulty === 'harder' ? 1 : 0;\n return { difficultyLevel: Math.min(5, Math.max(1, level + step)) };\n}\n\ninterface Build {\n request: AiVariantsRequest;\n ids: () => string;\n}\n\nconst scriptFrom = (value: unknown): { speaker: string; text: string }[] =>\n listOf(value).flatMap((turn) =>\n isRecord(turn) ? [{ speaker: stringOf(turn.speaker), text: stringOf(turn.text) }] : [],\n );\n\n/**\n * A variant's media in place of the exemplar's: a picture described anew, or\n * kept with `reuse`; a recording with no file yet — the host records it — its\n * playback policy, and the words the model wrote for it. An address, captions,\n * tracks and a poster belong to the old recording and are not copied.\n */\nfunction variantMedia(\n exemplar: Fields,\n wrote: unknown,\n ownText: boolean,\n imagePolicy: 'describe' | 'reuse',\n): Fields {\n const model = isRecord(wrote) ? wrote : {};\n if (isImage(exemplar)) {\n return imagePolicy === 'reuse'\n ? pick(exemplar, ['type', 'url', 'alt'])\n : { type: 'image', url: null, alt: stringOf(model.alt) };\n }\n const media: Fields = { type: exemplar.type, url: null, ...pick(exemplar, ['playback']) };\n if (!ownText) {\n if (hasScript(exemplar)) {\n media.script = scriptFrom(model.script);\n } else if (typeof exemplar.transcript === 'string') {\n media.transcript = stringOf(model.transcript);\n }\n }\n return media;\n}\n\n/** The feedback the model wrote, on the keys the exemplar's feedback has. */\nfunction feedbackFrom(exemplar: Fields, wrote: Fields): Fields {\n if (!isRecord(exemplar.feedback) || !isRecord(wrote.feedback)) {\n return {};\n }\n const out: Fields = {};\n for (const name of ['correct', 'incorrect']) {\n const said = written(wrote.feedback[name]);\n if (exemplar.feedback[name] !== undefined && said !== undefined) {\n out[name] = said;\n }\n }\n return Object.keys(out).length > 0 ? { feedback: out } : {};\n}\n\nfunction difficultyFor(\n request: AiVariantsRequest,\n exemplarIndex: number,\n item?: number,\n): AiVariantDifficulty {\n const targets = request.facts.exemplars.find((one) => one.exemplar === exemplarIndex)?.targets;\n const own =\n item !== undefined ? targets?.itemDifficulty?.find((one) => one.item === item) : undefined;\n return own?.difficulty ?? targets?.difficulty ?? request.facts.difficulty;\n}\n\nfunction buildActivity(\n exemplar: Fields,\n wrote: Fields,\n type: string,\n build: Build,\n difficulty: AiVariantDifficulty,\n id: string,\n): Fields {\n const { request, ids } = build;\n const imagePolicy = request.facts.imagePolicy;\n const alike = exemplar.type === type;\n const content: Fields = { title: stringOf(wrote.title) };\n if (typeof exemplar.instructions === 'string') {\n content.instructions = written(wrote.instructions) ?? exemplar.instructions;\n }\n switch (type) {\n case 'multiple-choice': {\n const before = listOf(exemplar.options);\n const withFeedback = before.some(\n (option) => isRecord(option) && option.feedback !== undefined,\n );\n content.question = stringOf(wrote.question);\n content.options = listOf(wrote.options).map((option, index) => {\n const fields = isRecord(option) ? option : {};\n const was = before[index];\n const feedback = withFeedback ? written(fields.feedback) : undefined;\n return {\n id: ids(),\n text: stringOf(fields.text),\n isCorrect: fields.isCorrect === true,\n ...(feedback !== undefined ? { feedback } : {}),\n ...(isRecord(was) && isRecord(was.media)\n ? { media: variantMedia(was.media, fields.media, false, imagePolicy) }\n : {}),\n };\n });\n break;\n }\n case 'fill-in-the-blanks': {\n const before = alike ? inPassageOrder(exemplar, 'blanks').map((one) => one.value) : [];\n content.passage = stringOf(wrote.passage);\n content.blanks = listOf(wrote.blanks).map((blank, index) => {\n const fields = isRecord(blank) ? blank : {};\n const was = isRecord(before[index]) ? (before[index] as Fields) : {};\n const hint = was.hint !== undefined ? written(fields.hint) : undefined;\n const feedback = was.feedback !== undefined ? written(fields.feedback) : undefined;\n return {\n id: String(index + 1),\n acceptedAnswers: textList(fields.acceptedAnswers),\n ...(hint !== undefined ? { hint } : {}),\n ...(feedback !== undefined ? { feedback } : {}),\n ...pick(was, ['match', 'caseSensitive', 'trimWhitespace']),\n };\n });\n break;\n }\n case 'gap-select': {\n const beforeGaps = alike ? inPassageOrder(exemplar, 'gaps').map((one) => one.value) : [];\n const beforeBanks = alike ? listOf(exemplar.banks) : [];\n const banks = beforeBanks.length\n ? listOf(wrote.banks).map((bank) => ({\n id: ids(),\n choices: listOf(isRecord(bank) ? bank.choices : undefined).map((choice) => ({\n id: ids(),\n text:\n typeof choice === 'string' ? choice : isRecord(choice) ? stringOf(choice.text) : '',\n })),\n }))\n : [];\n content.passage = stringOf(wrote.passage);\n content.gaps = listOf(wrote.gaps).map((gap, index) => {\n const fields = isRecord(gap) ? gap : {};\n const was = isRecord(beforeGaps[index]) ? (beforeGaps[index] as Fields) : {};\n const feedback = was.feedback !== undefined ? written(fields.feedback) : undefined;\n const drawsOn =\n listOf(was.choices).length === 0\n ? beforeBanks.findIndex((bank) => isRecord(bank) && bank.id === was.bankId)\n : -1;\n const bank = drawsOn !== -1 ? banks[drawsOn] : undefined;\n if (bank !== undefined) {\n const answer = stringOf(fields.answer).trim().toLowerCase();\n const right = bank.choices.find((choice) => choice.text.trim().toLowerCase() === answer);\n return {\n id: String(index + 1),\n bankId: bank.id,\n correctChoiceId: answer !== '' && right !== undefined ? right.id : '',\n ...(feedback !== undefined ? { feedback } : {}),\n };\n }\n const marked = listOf(fields.choices);\n const choices = marked.map((choice) => ({\n id: ids(),\n text: isRecord(choice) ? stringOf(choice.text) : '',\n }));\n // Exactly one marked right is a key; none, or several, is no key —\n // which validateDraft asks for, and a repair then supplies.\n const right = marked.flatMap((choice, at) =>\n isRecord(choice) && choice.isCorrect === true ? [at] : [],\n );\n return {\n id: String(index + 1),\n choices,\n correctChoiceId: right.length === 1 ? (choices[right[0] as number]?.id as string) : '',\n ...(feedback !== undefined ? { feedback } : {}),\n };\n });\n if (banks.length > 0) {\n content.banks = banks;\n }\n break;\n }\n case 'dictation':\n content.transcript = stringOf(wrote.transcript);\n if (Array.isArray(exemplar.acceptedTranscripts) && Array.isArray(wrote.acceptedTranscripts)) {\n const accepted = textList(wrote.acceptedTranscripts).filter((one) => one.trim() !== '');\n if (accepted.length > 0) {\n content.acceptedTranscripts = accepted;\n }\n }\n break;\n case 'read-aloud':\n content.referenceText = stringOf(wrote.referenceText);\n break;\n default:\n content.prompt = stringOf(wrote.prompt);\n }\n const ownText = type === 'dictation' || type === 'read-aloud';\n return {\n ...pick(exemplar, alike ? [...COPIED_COMMON, ...(COPIED[type] ?? [])] : COPIED_COMMON),\n ...steppedLevel(exemplar.difficultyLevel, difficulty),\n ...request.settings?.[type as keyof AiDraftSettings],\n ...content,\n ...(isRecord(exemplar.media)\n ? { media: variantMedia(exemplar.media, wrote.media, ownText, imagePolicy) }\n : {}),\n ...(ownText && isRecord(exemplar.slowMedia) ? { slowMedia: { type: 'audio', url: null } } : {}),\n ...feedbackFrom(exemplar, wrote),\n schemaVersion: typeof exemplar.schemaVersion === 'string' ? exemplar.schemaVersion : '1.0',\n type,\n id,\n };\n}\n\nfunction buildStimulus(exemplar: Fields, wrote: unknown, build: Build): Fields {\n const model = isRecord(wrote) ? wrote : {};\n const ownText = hasScript(exemplar) || typeof exemplar.transcript === 'string';\n const title = typeof exemplar.title === 'string' ? written(model.title) : undefined;\n return {\n id: build.ids(),\n kind: exemplar.kind,\n ...(title !== undefined ? { title } : {}),\n ...(typeof exemplar.body === 'string' ? { body: stringOf(model.body) } : {}),\n ...(hasScript(exemplar)\n ? { script: scriptFrom(model.script) }\n : typeof exemplar.transcript === 'string'\n ? { transcript: stringOf(model.transcript) }\n : {}),\n ...(isRecord(exemplar.media)\n ? { media: variantMedia(exemplar.media, model, ownText, build.request.facts.imagePolicy) }\n : {}),\n ...pick(exemplar, ['locale']),\n };\n}\n\nfunction buildEntry(\n exemplar: Fields,\n exemplarIndex: number,\n wrote: Fields,\n build: Build,\n id: string,\n): Fields {\n const difficulty = difficultyFor(build.request, exemplarIndex);\n if (exemplar.type !== 'item-group') {\n return buildActivity(exemplar, wrote, stringOf(exemplar.type), build, difficulty, id);\n }\n const before = listOf(exemplar.items);\n const title =\n typeof exemplar.title === 'string' ? (written(wrote.title) ?? exemplar.title) : undefined;\n return {\n ...pick(exemplar, ['slotKey', 'shuffle']),\n ...(title !== undefined ? { title } : {}),\n stimulus: buildStimulus(\n isRecord(exemplar.stimulus) ? exemplar.stimulus : {},\n wrote.stimulus,\n build,\n ),\n items: listOf(wrote.items).map((item, index) => {\n const fields = isRecord(item) ? item : {};\n const was = isRecord(before[index]) ? (before[index] as Fields) : {};\n const type = VARIANT_TYPES.includes(stringOf(fields.type))\n ? stringOf(fields.type)\n : stringOf(was.type);\n return buildActivity(\n was,\n fields,\n type,\n build,\n difficultyFor(build.request, exemplarIndex, index),\n build.ids(),\n );\n }),\n schemaVersion: typeof exemplar.schemaVersion === 'string' ? exemplar.schemaVersion : '1.0',\n type: 'item-group',\n id,\n };\n}\n\n// ── Checking a variant ─────────────────────────────────────────────────\n\nfunction variantFinding(\n code: 'variant_length' | 'variant_near_copy' | 'variant_same_key',\n path: readonly string[],\n message: string,\n): ItemFinding {\n return { path: [...path], message, code, severity: AI_VARIANT_CODE_SEVERITY[code] };\n}\n\n/** The items of an entry with their paths: the entry itself, or a group's items. */\nconst itemsWithPaths = (entry: unknown): { path: string[]; item: unknown }[] =>\n isRecord(entry) && entry.type === 'item-group'\n ? listOf(entry.items).map((item, index) => ({ path: ['items', String(index)], item }))\n : [{ path: [], item: entry }];\n\n/** The other forms' entries in this exemplar's place: the same `slotKey`, or the same position in each form. */\nfunction avoidedFor(\n request: AiVariantsRequest,\n exemplarIndex: number,\n): { entry: unknown; label: string }[] {\n const avoid = request.avoid ?? [];\n const exemplar = request.exemplars[exemplarIndex];\n const slotKey = isRecord(exemplar) ? exemplar.slotKey : undefined;\n const forms = avoid.length % request.exemplars.length === 0;\n return avoid.flatMap((entry, index) => {\n const theirs = isRecord(entry) ? entry.slotKey : undefined;\n const paired =\n slotKey !== undefined && theirs !== undefined\n ? slotKey === theirs\n : slotKey === undefined &&\n theirs === undefined &&\n forms &&\n index % request.exemplars.length === exemplarIndex;\n return paired ? [{ entry, label: `avoid[${index}]` }] : [];\n });\n}\n\nfunction nearCopies(entry: Fields, exemplar: Fields, request: AiVariantsRequest): ItemFinding[] {\n const mine = new Map(textsOf(exemplar).map((one) => [one.path.join('.'), one]));\n const avoided = (request.avoid ?? []).flatMap((other, index) =>\n textsOf(other).map((one) => ({ ...one, label: `avoid[${index}]` })),\n );\n const findings: ItemFinding[] = [];\n for (const one of textsOf(entry)) {\n let best: { score: number; what: string } | undefined;\n const consider = (score: number, what: string) => {\n if (score >= AI_VARIANT_NEAR_COPY_THRESHOLD && (best === undefined || score > best.score)) {\n best = { score, what };\n }\n };\n const theirs = mine.get(one.path.join('.'));\n if (theirs !== undefined) {\n consider(textSimilarity(theirs.text, one.text), `the exemplar's ${theirs.path.join('.')}`);\n }\n for (const other of avoided) {\n if (other.kind === one.kind) {\n consider(\n textSimilarity(other.text, one.text),\n `${other.label} ${other.path.join('.') || 'text'}`,\n );\n }\n }\n if (best !== undefined) {\n findings.push(\n variantFinding(\n 'variant_near_copy',\n one.path,\n `This reads like ${best.what} (similarity ${shownScore(best.score)}). Write it in new words.`,\n ),\n );\n }\n }\n const exemplarItems = new Map(\n itemsWithPaths(exemplar).map((one) => [one.path.join('.'), one.item]),\n );\n const avoidedOptions = (request.avoid ?? []).flatMap((other, index) =>\n itemsWithPaths(other).map((one) => ({ set: optionSet(one.item), label: `avoid[${index}]` })),\n );\n for (const { path, item } of itemsWithPaths(entry)) {\n const options = optionSet(item);\n if (options === null) {\n continue;\n }\n const sameAs = (other: string[] | null) =>\n other !== null && other.join('\\n') === options.join('\\n');\n const source = sameAs(optionSet(exemplarItems.get(path.join('.'))))\n ? \"the exemplar's\"\n : avoidedOptions.find((other) => sameAs(other.set))?.label;\n if (source !== undefined) {\n findings.push(\n variantFinding(\n 'variant_near_copy',\n [...path, 'options'],\n `These are ${source === \"the exemplar's\" ? \"the exemplar's options\" : `${source}'s options`}, in another order. Write new options.`,\n ),\n );\n }\n }\n return findings;\n}\n\nfunction sameKeys(\n entry: Fields,\n exemplar: Fields,\n exemplarIndex: number,\n request: AiVariantsRequest,\n): ItemFinding[] {\n const against = [\n { entry: exemplar as unknown, label: 'the exemplar' },\n ...avoidedFor(request, exemplarIndex),\n ];\n const findings: ItemFinding[] = [];\n for (const { path, item } of itemsWithPaths(entry)) {\n const slots = keysOf(item);\n const reported = new Set<string>();\n for (const other of against) {\n const theirs = itemsWithPaths(other.entry).find(\n (one) => one.path.join('.') === path.join('.'),\n );\n const theirSlots = keysOf(theirs?.item);\n for (const slot of slots) {\n const match = theirSlots.find((one) => one.slot === slot.slot);\n const shared = match !== undefined ? sharedAnswers(slot, match) : [];\n if (shared.length > 0 && !reported.has(slot.slot)) {\n reported.add(slot.slot);\n findings.push(\n variantFinding(\n 'variant_same_key',\n [...path, ...slot.path],\n `The right answer \"${shared[0]}\" is ${other.label === 'the exemplar' ? \"the exemplar's\" : `${other.label}'s`} too: a learner who sat that form scores again by memory.`,\n ),\n );\n }\n }\n }\n }\n return findings;\n}\n\n/** Whether a recording the exemplar's item has words for came back with none. */\nfunction wordlessRecordings(exemplar: Fields, entry: Fields): AiVariantMismatch[] {\n const mismatch = (path: string[], message: string): AiVariantMismatch => ({\n path,\n message,\n code: 'variant_structure',\n severity: 'invalid',\n });\n const out: AiVariantMismatch[] = [];\n const pairsOf = (a: unknown, b: unknown, at: string[]) => {\n if (!isRecord(a) || !isRecord(b) || a.type === 'dictation' || a.type === 'read-aloud') {\n return;\n }\n if (isRecording(a.media) && transcriptOf(a.media) !== null && transcriptOf(b.media) === null) {\n out.push(\n mismatch(\n [...at, 'media', hasScript(a.media as Fields) ? 'script' : 'transcript'],\n 'The recording has no words to record: write what it says.',\n ),\n );\n }\n };\n if (exemplar.type === 'item-group') {\n const before = isRecord(exemplar.stimulus) ? exemplar.stimulus : {};\n const after = isRecord(entry.stimulus) ? entry.stimulus : {};\n if (transcriptOf(before) !== null && transcriptOf(after) === null) {\n out.push(\n mismatch(\n [\n 'stimulus',\n hasScript(before) || (isRecord(before.media) && hasScript(before.media))\n ? 'script'\n : 'transcript',\n ],\n 'The recording has no words to record: write what it says.',\n ),\n );\n }\n listOf(exemplar.items).forEach((item, index) => {\n pairsOf(item, listOf(entry.items)[index], ['items', String(index)]);\n });\n } else {\n pairsOf(exemplar, entry, []);\n }\n return out;\n}\n\nfunction pairsOf(exemplar: Fields, entry: Fields): AiVariantPair[] {\n const top: AiVariantPair = {\n exemplarPath: [],\n variantPath: [],\n exemplarId: stringOf(exemplar.id),\n variantId: stringOf(entry.id),\n };\n if (exemplar.type !== 'item-group') {\n return [top];\n }\n const theirs = listOf(entry.items);\n return [\n top,\n ...listOf(exemplar.items).flatMap((item, index) =>\n index < theirs.length\n ? [\n {\n exemplarPath: ['items', String(index)],\n variantPath: ['items', String(index)],\n exemplarId: idOf(item) ?? '',\n variantId: idOf(theirs[index]) ?? '',\n },\n ]\n : [],\n ),\n ];\n}\n\ninterface Built {\n exemplarIndex: number;\n wroteType: string;\n entry: Fields;\n generated: Fields;\n}\n\nfunction evaluate(built: Built, request: AiVariantsRequest): AiGeneratedVariant {\n const exemplar = request.exemplars[built.exemplarIndex] as Fields;\n const type = stringOf(exemplar.type);\n const { entry } = built;\n const group = type === 'item-group';\n const validation = group\n ? validateItemGroupDraft(entry)\n : validateDraft(type as ActivityType, entry);\n const mismatches: AiVariantMismatch[] =\n built.wroteType !== type\n ? [\n {\n path: ['type'],\n message: `This was written as ${built.wroteType || 'no type'}; the exemplar is ${type}.`,\n code: 'variant_type',\n severity: 'invalid',\n },\n ]\n : [\n ...structureMisses(exemplar, entry).map(\n (miss): AiVariantMismatch => ({\n ...miss,\n code: 'variant_structure',\n severity: 'invalid',\n }),\n ),\n ...wordlessRecordings(exemplar, entry),\n ];\n const findings = [\n ...(group ? critiqueItemGroupDraft(entry) : critiqueDraft(type as ActivityType, entry)),\n ...lengthMisses(exemplar, entry, request.lengthTolerance).map((miss) =>\n variantFinding(\n 'variant_length',\n miss.path,\n `${miss.words} words; the exemplar's has ${miss.exemplarWords}, so ${miss.bounds[0]} to ${miss.bounds[1]}.`,\n ),\n ),\n ...nearCopies(entry, exemplar, request),\n ...sameKeys(entry, exemplar, built.exemplarIndex, request),\n ];\n const status =\n validation.status === 'invalid' || mismatches.length > 0\n ? 'invalid'\n : validation.status === 'incomplete'\n ? 'incomplete'\n : 'complete';\n return {\n exemplarIndex: built.exemplarIndex,\n type,\n entry,\n status,\n validation,\n mismatches,\n findings,\n pairs: pairsOf(exemplar, entry),\n generated: built.generated,\n };\n}\n\n// ── Keys spread across positions ───────────────────────────────────────\n\n/**\n * Moves each single-answer, unshuffled multiple choice's right option to the\n * position used least so far across the variants, ties broken by an order\n * drawn from `seed`: as even as the counts allow, and the same every time.\n * An option moves with its id, text, feedback and media.\n */\nfunction balanceKeys(built: readonly Built[], seed: string): Built[] {\n const counts = new Map<number, number>();\n let slot = 0;\n const visit = (item: unknown): unknown => {\n if (\n !isRecord(item) ||\n item.type !== 'multiple-choice' ||\n item.mode !== 'single' ||\n item.shuffle === true\n ) {\n return item;\n }\n const options = listOf(item.options);\n const keys = options.flatMap((option, index) =>\n isRecord(option) && option.isCorrect === true ? [index] : [],\n );\n if (keys.length !== 1) {\n return item;\n }\n const order = seededShuffle(\n options.map((_, index) => index),\n `${seed}:${slot}`,\n { version: 2 },\n );\n slot += 1;\n let target = order[0] as number;\n for (const position of order) {\n if ((counts.get(position) ?? 0) < (counts.get(target) ?? 0)) {\n target = position;\n }\n }\n counts.set(target, (counts.get(target) ?? 0) + 1);\n const key = options[keys[0] as number];\n const rest = options.filter((_, index) => index !== keys[0]);\n return { ...item, options: [...rest.slice(0, target), key, ...rest.slice(target)] };\n };\n return [...built]\n .sort((a, b) => a.exemplarIndex - b.exemplarIndex)\n .map((one) => ({\n ...one,\n entry:\n one.entry.type === 'item-group'\n ? { ...one.entry, items: listOf(one.entry.items).map(visit) }\n : (visit(one.entry) as Fields),\n }));\n}\n\n// ── Reading a reply ────────────────────────────────────────────────────\n\n/** Ids from the host, checked as `createDraft` checks them. */\nfunction idSource(newId: () => string, caller: string): () => string {\n const issued = new Set<string>();\n return () => {\n const id: unknown = newId();\n if (typeof id !== 'string' || id === '') {\n throw new Error(`${caller}: newId() must return a non-empty string.`);\n }\n if (issued.has(id)) {\n throw new Error(\n `${caller}: newId() returned \"${id}\" twice. Ids must differ — two options sharing an id cannot be scored apart.`,\n );\n }\n issued.add(id);\n return id;\n };\n}\n\ntype Read =\n | { ok: false; refusal: AiRefusal }\n | { ok: true; built: Built[]; missing: AiVariantMissing[]; raw: Fields };\n\nfunction missingOf(exemplarIndex: number): AiVariantMissing {\n return {\n exemplarIndex,\n code: 'variant_missing',\n message: `The reply has no variant of exemplar ${exemplarIndex}.`,\n severity: 'invalid',\n };\n}\n\nfunction read(\n raw: unknown,\n request: AiVariantsRequest,\n ids: () => string,\n keep: ReadonlyMap<number, string>,\n): Read {\n if (!isRecord(raw) || !Array.isArray(raw.variants) || !raw.variants.every(isRecord)) {\n return { ok: false, refusal: 'malformed' };\n }\n const asked = request.repair\n ? request.repair.map((one) => one.exemplar)\n : request.facts.exemplars.map((one) => one.exemplar);\n if (raw.variants.length > asked.length) {\n return { ok: false, refusal: 'too-long' };\n }\n const seen = new Set<number>();\n for (const variant of raw.variants as Fields[]) {\n const index = variant.exemplar;\n if (typeof index !== 'number' || !asked.includes(index) || seen.has(index)) {\n return { ok: false, refusal: 'malformed' };\n }\n seen.add(index);\n }\n const build: Build = { request, ids };\n const built = (raw.variants as Fields[]).map((variant): Built => {\n const exemplarIndex = variant.exemplar as number;\n const exemplar = request.exemplars[exemplarIndex] as Fields;\n return {\n exemplarIndex,\n wroteType: stringOf(variant.type),\n entry: buildEntry(exemplar, exemplarIndex, variant, build, keep.get(exemplarIndex) ?? ids()),\n generated: variant,\n };\n });\n return {\n ok: true,\n built: built.sort((a, b) => a.exemplarIndex - b.exemplarIndex),\n missing: asked\n .filter((index) => !seen.has(index))\n .sort((a, b) => a - b)\n .map(missingOf),\n raw,\n };\n}\n\nfunction setFindings(variants: readonly AiGeneratedVariant[]): ItemFinding[] {\n const entries = variants.map((one) => one.entry);\n return [...keyPositionFinding(entries, []), ...paperFindings(entries)];\n}\n\n/**\n * Reads a model's reply to {@link aiVariantsRequest} into variants an editor\n * can show for approval: each made from what the model wrote and what its\n * exemplar is — type, structure, scoring and settings copied from the\n * exemplar, whatever the reply says of them — with ids from `newId`, checked\n * by `validateDraft` (or `validateItemGroupDraft`), compared with its exemplar\n * (`mismatches`: `variant_type`, `variant_structure`), critiqued, and checked\n * for length, near copies and repeated keys against the exemplar and every\n * entry in `avoid` (`findings`: `variant_length`, `variant_near_copy`,\n * `variant_same_key`). `pairs` line each exemplar item up with its variant's.\n *\n * A recording comes back with no file — `url: null`, its playback policy, and\n * a new script or transcript — and a picture to describe with no file and a\n * new `alt`: `media_url_required` until the host attaches one.\n *\n * Variants are independent: one that is wrong comes back as such, beside the\n * others. An exemplar the reply wrote nothing for is listed in `missing`. The\n * whole reply is refused only when it is not `{ variants: [...] }` of objects\n * each naming an exemplar of the facts once (`malformed`), or holds more\n * variants than exemplars (`too-long`).\n *\n * With `balanceKeyPositions` the single-answer keys are spread across\n * positions, from its seed.\n *\n * @throws Error when `newId` returns an empty or repeated id.\n */\nexport function checkAiVariants(\n raw: unknown,\n request: AiVariantsRequest,\n options: { newId: () => string },\n): { ok: true; variants: AiVariants } | { ok: false; refusal: AiRefusal } {\n const result = read(raw, request, idSource(options.newId, 'checkAiVariants'), new Map());\n if (!result.ok) {\n return result;\n }\n const seed = request.balanceKeyPositions?.seed;\n const built = seed !== undefined ? balanceKeys(result.built, seed) : result.built;\n const variants = built.map((one) => evaluate(one, request));\n const provenance = provenanceOf(result.raw.provenance);\n const usage = usageOf(result.raw.usage);\n return {\n ok: true,\n variants: {\n variants,\n missing: result.missing,\n findings: setFindings(variants),\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n\n// ── Repair ─────────────────────────────────────────────────────────────\n\n/** The fields a model writes, and inside media and material, the ones it writes there. */\nconst WRITTEN_FIELDS = new Set([\n 'type',\n 'title',\n 'instructions',\n 'question',\n 'options',\n 'passage',\n 'blanks',\n 'gaps',\n 'banks',\n 'transcript',\n 'acceptedTranscripts',\n 'referenceText',\n 'prompt',\n 'feedback',\n 'media',\n 'stimulus',\n 'items',\n]);\nconst WRITTEN_IN_MEDIA = new Set(['alt', 'transcript', 'script']);\nconst WRITTEN_IN_STIMULUS = new Set(['title', 'body', 'transcript', 'script', 'media', 'alt']);\n\nfunction writable(path: readonly string[]): boolean {\n const rest = path[0] === 'items' && path.length > 2 ? path.slice(2) : path;\n const [first, second, third] = rest;\n if (first === undefined || !WRITTEN_FIELDS.has(first)) {\n return false;\n }\n if (first === 'media') {\n return second !== undefined && WRITTEN_IN_MEDIA.has(second);\n }\n if (first === 'stimulus') {\n if (second === 'media') {\n return third !== undefined && WRITTEN_IN_MEDIA.has(third);\n }\n return second !== undefined && WRITTEN_IN_STIMULUS.has(second);\n }\n if (first === 'options' && rest[2] === 'media') {\n return rest[3] !== undefined && WRITTEN_IN_MEDIA.has(rest[3]);\n }\n return true;\n}\n\n/** Where the model wrote what a problem names. */\nfunction pathForModel(variant: AiGeneratedVariant, problem: AiDraftProblem): string[] {\n const { path, code } = problem;\n const prefix = path[0] === 'items' && path.length > 2 ? path.slice(0, 2) : [];\n const rest = path.slice(prefix.length);\n if (code === 'ra_text_long_for_time') {\n return [...prefix, 'referenceText'];\n }\n if (rest[0] === 'gaps' && rest.length === 3 && rest[2] === 'correctChoiceId') {\n const item = prefix.length > 0 ? listOf(variant.entry.items)[Number(prefix[1])] : variant.entry;\n const gap = isRecord(item) ? listOf(item.gaps)[Number(rest[1])] : undefined;\n return [\n ...prefix,\n 'gaps',\n rest[1] as string,\n isRecord(gap) && gap.bankId !== undefined ? 'answer' : 'choices',\n ];\n }\n if (\n rest[0] === 'stimulus' &&\n rest[1] === 'media' &&\n (rest[2] === 'script' || rest[2] === 'transcript')\n ) {\n return ['stimulus', rest[2]];\n }\n return [...path];\n}\n\nfunction problemsOf(variant: AiGeneratedVariant): AiDraftProblem[] {\n return [\n ...variant.mismatches.map(\n (found): AiDraftProblem => ({\n path: found.path,\n code: found.code,\n message: found.message,\n severity: 'invalid',\n }),\n ),\n ...variant.validation.issues\n .filter((found) => found.code !== 'media_url_required')\n .map(\n (found): AiDraftProblem => ({\n path: found.path,\n code: found.code,\n message: found.message,\n severity: found.severity,\n }),\n ),\n ...variant.findings\n .filter((found) => found.severity === 'warning')\n .map(\n (found): AiDraftProblem => ({\n path: found.path,\n code: found.code,\n message: found.message,\n severity: 'warning',\n }),\n ),\n ]\n .map((problem) => ({ ...problem, path: pathForModel(variant, problem) }))\n .filter((problem) => writable(problem.path));\n}\n\n/**\n * A second request, for the variants with something the model can fix —\n * a wrong type or structure, an issue other than a missing file, or a warning\n * at a field the model writes — and the exemplars the reply wrote nothing for\n * (`variant_missing`). Each comes with its problems at the paths the model\n * wrote; the exemplar and its targets stay in the facts. `null` when there is\n * nothing to repair.\n *\n * The reply holds one variant for each, naming its exemplar;\n * {@link checkAiVariants} reads it.\n */\nexport function aiVariantsRepairRequest(\n request: AiVariantsRequest,\n variants: AiVariants | AiVariantsRun | readonly AiGeneratedVariant[],\n): AiVariantsRequest | null {\n const list: readonly AiGeneratedVariant[] = Array.isArray(variants)\n ? (variants as readonly AiGeneratedVariant[])\n : (variants as AiVariants).variants;\n const missing: readonly AiVariantMissing[] = Array.isArray(variants)\n ? []\n : (variants as AiVariants).missing;\n const repair = [\n ...list.flatMap((one) => {\n const problems = problemsOf(one);\n return problems.length === 0\n ? []\n : [{ exemplar: one.exemplarIndex, variant: one.generated, problems }];\n }),\n ...missing.map((one) => ({\n exemplar: one.exemplarIndex,\n problems: [{ path: [], code: one.code, message: one.message, severity: 'invalid' as const }],\n })),\n ].sort((a, b) => a.exemplar - b.exemplar);\n if (repair.length === 0) {\n return null;\n }\n const { repair: _previous, ...first } = request;\n return {\n ...first,\n shape: replyShape(\n repair.map((one) => request.exemplars[one.exemplar] as Fields),\n repair.length,\n ),\n repair,\n };\n}\n\n// ── The loop ───────────────────────────────────────────────────────────\n\nconst RANK = { complete: 2, incomplete: 1, invalid: 0 } as const;\n\nconst invalidCount = (one: AiGeneratedVariant): number =>\n one.mismatches.length +\n one.validation.issues.filter((found) => found.severity === 'invalid').length;\nconst warningCount = (one: AiGeneratedVariant): number =>\n one.findings.filter((found) => found.severity === 'warning').length;\n\n/** Whether a repaired variant is at least as good as the one it would replace. */\nfunction noWorse(repaired: AiGeneratedVariant, before: AiGeneratedVariant): boolean {\n const now = RANK[repaired.status];\n const was = RANK[before.status];\n if (now !== was) {\n return now > was;\n }\n if (invalidCount(repaired) !== invalidCount(before)) {\n return invalidCount(repaired) < invalidCount(before);\n }\n return warningCount(repaired) <= warningCount(before);\n}\n\n/**\n * Writes variants of existing items, with a bounded repair loop: asks the port\n * with `request`, reads the reply as {@link checkAiVariants} does, then — up\n * to `repairs` times — sends back what the model can fix and what it did not\n * write ({@link aiVariantsRepairRequest}), keeping each variant's best pass:\n * by status, then fewer invalid problems, then fewer warnings. A repaired\n * variant keeps its id. With `balanceKeyPositions`, the keys are spread once,\n * over the variants as they end.\n *\n * Never throws for a model's failure: a port that throws or a reply the SDK\n * refuses ends the loop, and is recorded in `calls` beside the provenance and\n * usage of every call. A first call that fails leaves no variants. A request\n * with no exemplar to vary makes no call.\n *\n * ```ts\n * const run = await generateVariants({\n * request: aiVariantsRequest({ exemplars: section, level: 'CEFR B1', avoid: otherForms }),\n * port: (request) => callMyModel(VARIANTS_PROMPT, request),\n * newId: () => crypto.randomUUID(),\n * });\n * // run.variants: store each as proposed, for a person to approve.\n * ```\n *\n * @throws RangeError when `repairs` is not a whole number from 0 to\n * {@link AI_VARIANTS_MAX_REPAIRS} (default 1), and Error when `newId` returns\n * an empty or repeated id.\n */\nexport async function generateVariants(input: {\n request: AiVariantsRequest;\n port: (request: AiVariantsRequest) => Promise<unknown> | unknown;\n newId: () => string;\n repairs?: number;\n}): Promise<AiVariantsRun> {\n const { request, port, newId } = input;\n const repairs = input.repairs ?? 1;\n if (!Number.isInteger(repairs) || repairs < 0 || repairs > AI_VARIANTS_MAX_REPAIRS) {\n throw new RangeError(\n `generateVariants: repairs must be a whole number from 0 to ${AI_VARIANTS_MAX_REPAIRS}, not ${String(repairs)}.`,\n );\n }\n const calls: AiDraftsCall[] = [];\n const skipped = request.skipped;\n if (request.facts.exemplars.length === 0) {\n return { variants: [], missing: [], findings: [], skipped, calls };\n }\n // One source of ids for the whole run, so no id is issued twice across passes.\n const ids = idSource(newId, 'generateVariants');\n\n const ask = async (\n asked: AiVariantsRequest,\n keep: ReadonlyMap<number, string>,\n ): Promise<\n { built: Built[]; variants: AiGeneratedVariant[]; missing: AiVariantMissing[] } | undefined\n > => {\n let raw: unknown;\n try {\n raw = await port(asked);\n } catch (error) {\n calls.push({ ok: false, error: error instanceof Error ? error.message : String(error) });\n return undefined;\n }\n const result = read(raw, asked, ids, keep);\n if (!result.ok) {\n calls.push({ ok: false, refusal: result.refusal });\n return undefined;\n }\n const provenance = provenanceOf(result.raw.provenance);\n const usage = usageOf(result.raw.usage);\n calls.push({\n ok: true,\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n });\n return {\n built: result.built,\n variants: result.built.map((one) => evaluate(one, request)),\n missing: result.missing,\n };\n };\n\n const first = await ask(request, new Map());\n if (first === undefined) {\n return { variants: [], missing: [], findings: [], skipped, calls };\n }\n const best = new Map<number, { built: Built; variant: AiGeneratedVariant }>();\n first.variants.forEach((variant, index) => {\n best.set(variant.exemplarIndex, { built: first.built[index] as Built, variant });\n });\n const wanted = request.facts.exemplars.map((one) => one.exemplar);\n const missingNow = () => wanted.filter((index) => !best.has(index)).map(missingOf);\n\n for (let pass = 0; pass < repairs; pass += 1) {\n const repairRequest = aiVariantsRepairRequest(request, {\n variants: [...best.values()].map((one) => one.variant),\n missing: missingNow(),\n findings: [],\n });\n if (repairRequest === null) {\n break;\n }\n const keep = new Map(\n (repairRequest.repair ?? []).flatMap((one) => {\n const before = best.get(one.exemplar);\n return before !== undefined\n ? [[one.exemplar, stringOf(before.variant.entry.id)] as const]\n : [];\n }),\n );\n const repaired = await ask(repairRequest, keep);\n if (repaired === undefined) {\n break;\n }\n repaired.variants.forEach((variant, index) => {\n const before = best.get(variant.exemplarIndex);\n if (before === undefined || noWorse(variant, before.variant)) {\n best.set(variant.exemplarIndex, { built: repaired.built[index] as Built, variant });\n }\n });\n }\n\n const ended = [...best.values()].sort(\n (a, b) => a.variant.exemplarIndex - b.variant.exemplarIndex,\n );\n const seed = request.balanceKeyPositions?.seed;\n const variants =\n seed !== undefined\n ? balanceKeys(\n ended.map((one) => one.built),\n seed,\n ).map((one) => evaluate(one, request))\n : ended.map((one) => one.variant);\n return { variants, missing: missingNow(), findings: setFindings(variants), skipped, calls };\n}\n","import { aiAllowedByContent, aiSupports, readAiTextResult } from './ai.js';\nimport { materialFacts } from './ai-material.js';\nimport {\n AI_WRITING_MAX_FIELD_LENGTH,\n checkCorrections,\n isRecord,\n MAX_TAG_LENGTH,\n textField,\n} from './ai-written.js';\nimport { countWords } from './count-words.js';\nimport { gradeFromRubric } from './grading.js';\nimport type { LearnerResponse, WrittenResponseData } from './types/activity.js';\nimport type {\n AiActivityInput,\n AiGroupInput,\n AiRefusal,\n AiRubricCriterionFact,\n AiWritingFeedback,\n AiWritingFeedbackRequest,\n} from './types/ai.js';\nimport type { CriterionScore } from './types/grading.js';\n\nexport { AI_WRITING_MAX_CORRECTIONS, AI_WRITING_MAX_FIELD_LENGTH } from './ai-written.js';\n\n/**\n * The request for feedback on a draft, or `null` when there is none to give:\n * not a written response, an author who switched explanations off (writing\n * feedback reads `ai.explanations`), a redacted item — feedback is for\n * practice, where the item is whole — or a draft with nothing in it.\n *\n * The facts are the task as the learner saw it, the draft verbatim with its\n * word count recomputed as the grader will count it, and the rubric the answer\n * will be graded on. `previousFeedback` is the text of the feedback already\n * shown on earlier drafts, so a model can speak to what changed. `group` is the\n * item group the task sits in: its material, the text a summary is of, goes\n * with the request as `material`.\n */\nexport function aiWritingFeedbackRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n previousFeedback?: readonly string[];\n group?: AiGroupInput;\n learnerLocale?: string;\n}): AiWritingFeedbackRequest | null {\n const { data, response, learnerLocale } = input;\n if (\n !aiSupports(data.type, 'writing-feedback') ||\n !aiAllowedByContent(data, 'writing-feedback') ||\n (data as { redacted?: unknown }).redacted === true\n ) {\n return null;\n }\n const text = response?.type === 'written-response' ? response.text : '';\n if (typeof text !== 'string' || text.trim() === '') {\n return null;\n }\n const item = data as unknown as WrittenResponseData;\n const wordCount = countWords(text);\n const minWords = typeof item.minWords === 'number' ? item.minWords : 0;\n const maxWords = typeof item.maxWords === 'number' ? item.maxWords : Number.POSITIVE_INFINITY;\n const criteria = Array.isArray(item.rubric?.criteria) ? item.rubric.criteria : null;\n const rubric: AiRubricCriterionFact[] | null =\n criteria === null || criteria.length === 0\n ? null\n : criteria.map((criterion) => ({\n name: criterion.name,\n ...(typeof criterion.description === 'string'\n ? { description: criterion.description }\n : {}),\n weight: criterion.weight,\n }));\n const previousFeedback = [...(input.previousFeedback ?? [])];\n const material = materialFacts(input.group);\n return {\n feature: 'writing-feedback',\n facts: {\n activityType: 'written-response',\n activityId: data.id,\n title: data.title,\n ...(typeof data.locale === 'string' ? { locale: data.locale } : {}),\n prompt: typeof item.prompt === 'string' ? item.prompt : '',\n text,\n wordCount,\n minWords,\n maxWords,\n withinWordBounds: wordCount >= minWords && wordCount <= maxWords,\n rubric,\n ...(typeof item.languageTarget === 'string' ? { languageTarget: item.languageTarget } : {}),\n },\n draftNumber: previousFeedback.length + 1,\n previousFeedback,\n ...(material !== null ? { material } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n };\n}\n\n/** A criterion score a grade could be computed from: finite, 0 or more, and not above its maximum. */\nfunction isJudgedScore(score: unknown, maxScore: unknown): boolean {\n if (score === undefined) {\n return maxScore === undefined || (typeof maxScore === 'number' && maxScore > 0);\n }\n const max = maxScore ?? 1;\n return (\n typeof score === 'number' &&\n typeof max === 'number' &&\n Number.isFinite(score) &&\n Number.isFinite(max) &&\n max > 0 &&\n score >= 0 &&\n score - max <= max * 1e-9\n );\n}\n\n/** The judgements, each on a criterion the author wrote and weighted as the author weighted it. */\nfunction checkCriteria(\n raw: unknown,\n rubric: readonly AiRubricCriterionFact[] | null,\n): { ok: true; criteria: CriterionScore[] } | { ok: false; refusal: AiRefusal } {\n if (raw === undefined || raw === null) {\n return { ok: true, criteria: [] };\n }\n if (!Array.isArray(raw)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (raw.length === 0) {\n return { ok: true, criteria: [] };\n }\n // Judging a criterion the author did not write is judging a different task.\n if (rubric === null) {\n return { ok: false, refusal: 'malformed' };\n }\n const weights = new Map(rubric.map((criterion) => [criterion.name, criterion.weight]));\n const seen = new Set<string>();\n const criteria: CriterionScore[] = [];\n for (const entry of raw) {\n if (!isRecord(entry) || typeof entry.name !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n const weight = weights.get(entry.name);\n if (weight === undefined || seen.has(entry.name)) {\n return { ok: false, refusal: 'malformed' };\n }\n seen.add(entry.name);\n if (!isJudgedScore(entry.score, entry.maxScore)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (entry.notApplicable !== undefined && typeof entry.notApplicable !== 'boolean') {\n return { ok: false, refusal: 'malformed' };\n }\n const band = textField(entry.band, MAX_TAG_LENGTH);\n const comment = textField(entry.comment, AI_WRITING_MAX_FIELD_LENGTH);\n if (!band.ok) {\n return band;\n }\n if (!comment.ok) {\n return comment;\n }\n criteria.push({\n name: entry.name,\n ...(typeof entry.score === 'number' ? { score: entry.score } : {}),\n ...(typeof entry.maxScore === 'number' ? { maxScore: entry.maxScore } : {}),\n ...(band.value !== undefined ? { band: band.value } : {}),\n ...(comment.value !== undefined ? { comment: comment.value } : {}),\n ...(entry.notApplicable === true ? { notApplicable: true } : {}),\n weight,\n });\n }\n return { ok: true, criteria };\n}\n\n/**\n * Checks writing feedback a port returned for `request`, and turns it into what\n * a learner is shown. The SDK shows a model's feedback whole or not at all: it\n * never drops the part it doubts and shows the rest.\n *\n * - **The text** is read as every AI text is (`readAiTextResult`): plain text,\n * not empty, at most {@link AI_TEXT_MAX_LENGTH} code points.\n * - **Every correction must quote the draft** — the check only this SDK can run.\n * The SDK finds each quote in the draft itself, typographic quotes and runs of\n * whitespace aside, and anchors it there; a range the model claims must hold\n * the quote. A correction of words the learner did not write refuses the\n * whole reply as `misquotes-answer`, as a hint that gives the answer away\n * refuses the hint. The `original` shown is the draft's own text at the range.\n * At most {@link AI_WRITING_MAX_CORRECTIONS}; a correction that changes\n * nothing is `malformed`.\n * - **Criteria are judgements; the arithmetic is the SDK's.** Each names a\n * criterion of the item's rubric, once; its score, when it has one, is a\n * score out of its `maxScore`. The rubric's weights are attached, whatever\n * the model sent. Anything else is `malformed`, as is any criterion on an\n * item with no rubric.\n * - **`indicativeScore`** is the rubric's weighted total, computed as\n * `gradeFromRubric` computes a grade, when every criterion of the rubric was\n * judged; `null` otherwise. An indication, never a grade.\n */\nexport function checkAiWritingFeedback(\n raw: unknown,\n request: AiWritingFeedbackRequest,\n): { ok: true; feedback: AiWritingFeedback } | { ok: false; refusal: AiRefusal } {\n const read = readAiTextResult(raw);\n if (!read.ok) {\n return read;\n }\n const record = raw as Record<string, unknown>;\n const corrections = checkCorrections(record.corrections, request.facts.text);\n if (!corrections.ok) {\n return corrections;\n }\n const rubric = request.facts.rubric;\n const criteria = checkCriteria(record.criteria, rubric);\n if (!criteria.ok) {\n return criteria;\n }\n let indicativeScore: number | null = null;\n const judged = new Set(criteria.criteria.map((criterion) => criterion.name));\n if (rubric?.every((criterion) => judged.has(criterion.name))) {\n const grade = gradeFromRubric(criteria.criteria);\n indicativeScore = 'unscorable' in grade ? null : grade.score;\n }\n const { text, provenance, usage } = read.result;\n return {\n ok: true,\n feedback: {\n text,\n corrections: corrections.corrections,\n criteria: criteria.criteria,\n indicativeScore,\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ai-writing-BaaiuU5t.js","names":["KINDS","isRecord","listOf","instructionsOf","isRecord","isRecord","written","turnsOf","isRecord","isRecord","listOf","stringOf","listOf","stringOf","isRecord","isRecord","KINDS","isRecord","isRecord","isRecord","text","WRITES","replyShape","stringOf","listOf","written","idSource","pathForModel","problemsOf","RANK","noWorse","isRecord","isRecord","isRecord"],"sources":["../src/ai-material.ts","../src/ai-text.ts","../src/ai.ts","../src/ai-coaching.ts","../src/media-refs.ts","../src/authoring/recordings.ts","../src/variant-rules.ts","../src/authoring/paper.ts","../src/authoring/item-group.ts","../src/authoring/index.ts","../src/ai-critique.ts","../src/ai-drafts.ts","../src/ai-written.ts","../src/ai-grading.ts","../src/ai-variants.ts","../src/ai-writing.ts"],"sourcesContent":["import type { ActivityMedia } from './types/activity.js';\nimport type { AiGroupInput, AiMaterialFacts } from './types/ai.js';\nimport type { StimulusKind } from './types/item-group.js';\n\n/**\n * What a model is told about the group a question sits in: the material the\n * learner reads and sees beside it.\n *\n * Internal: the request builders in `ai.ts` and `ai-writing.ts` are its public\n * face, through their `group` input.\n */\n\n/**\n * The longest material text a model is given, in code points. A longer one is\n * cut and marked `truncated`: a long reading passage on an exam paper runs to\n * 4,000–7,000.\n */\nexport const AI_MATERIAL_MAX_LENGTH = 12_000;\n\n// Every kind and every media type, so a new one does not compile until it is\n// read here.\nconst KINDS: Record<StimulusKind, true> = {\n text: true,\n audio: true,\n video: true,\n image: true,\n mixed: true,\n};\nconst MEDIA_TYPES: Record<ActivityMedia['type'], true> = {\n image: true,\n audio: true,\n video: true,\n embed: true,\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/** A string with something in it, as written; otherwise nothing. */\nconst filled = (value: unknown): string | undefined =>\n typeof value === 'string' && value.trim() !== '' ? value : undefined;\n\n/**\n * `text` cut to {@link AI_MATERIAL_MAX_LENGTH} code points. The cut falls at\n * the last space in its second half, so no word is broken; in a script written\n * without spaces, at the limit itself.\n */\nfunction cut(text: string): { text: string; truncated: boolean } {\n const points = [...text];\n if (points.length <= AI_MATERIAL_MAX_LENGTH) {\n return { text, truncated: false };\n }\n // From the code point just past the limit: a space there leaves every kept word whole.\n let end = AI_MATERIAL_MAX_LENGTH;\n for (let at = AI_MATERIAL_MAX_LENGTH; at >= AI_MATERIAL_MAX_LENGTH / 2; at -= 1) {\n if (/\\s/u.test(points[at] as string)) {\n end = at;\n break;\n }\n }\n return { text: points.slice(0, end).join('').trimEnd(), truncated: true };\n}\n\n/**\n * The facts of `group`'s material, or `null` without a group or a stimulus it\n * can read: not an object, or of a kind the SDK does not know.\n *\n * Only what the learner sees reaches a model. The `transcript` is author-only\n * and never read, so the facts are the same for a whole group and a redacted\n * one; a media address is never read either, since a model cannot open one\n * and a signed address must not end up in a prompt. Each field is read once.\n */\nexport function materialFacts(group: AiGroupInput | undefined): AiMaterialFacts | null {\n if (!isRecord(group)) {\n return null;\n }\n const stimulus = group.stimulus;\n if (!isRecord(stimulus)) {\n return null;\n }\n const kind = stimulus.kind;\n if (typeof kind !== 'string' || !Object.hasOwn(KINDS, kind)) {\n return null;\n }\n const title = filled(stimulus.title) ?? filled(group.title);\n const body = filled(stimulus.body);\n const text = body === undefined ? undefined : cut(body);\n const rawMedia = stimulus.media;\n const mediaType = isRecord(rawMedia) ? rawMedia.type : undefined;\n const alt = isRecord(rawMedia) ? filled(rawMedia.alt) : undefined;\n const media =\n typeof mediaType === 'string' && Object.hasOwn(MEDIA_TYPES, mediaType)\n ? { type: mediaType as ActivityMedia['type'], ...(alt !== undefined ? { alt } : {}) }\n : undefined;\n const attribution = filled(stimulus.attribution);\n const locale = filled(stimulus.locale);\n return {\n kind: kind as StimulusKind,\n ...(title !== undefined ? { title } : {}),\n ...(text !== undefined ? { text: text.text } : {}),\n ...(text?.truncated === true ? { truncated: true as const } : {}),\n ...(media !== undefined ? { media } : {}),\n ...(attribution !== undefined ? { attribution } : {}),\n ...(locale !== undefined ? { locale } : {}),\n };\n}\n","import type { AiProvenance } from './types/ai.js';\nimport type { GraderUsage } from './types/grading.js';\n\n/**\n * How the SDK reads text a model wrote, before a learner does. Internal: shared\n * by the AI checks, and exported by none of the package's entry points.\n */\n\n/**\n * Plain text as the learner will read it: line endings unified, control\n * characters other than line breaks and tabs removed, runs of blank lines\n * collapsed, the ends trimmed.\n */\nexport function cleanText(text: string): string {\n return (\n text\n .replace(/\\r\\n?/g, '\\n')\n // biome-ignore lint/suspicious/noControlCharactersInRegex: removing them is the point\n .replace(/[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F\\u2028\\u2029]/g, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n );\n}\n\n/** A provenance field a host may send, kept only when it is a short string. */\nexport function provenanceOf(raw: unknown): AiProvenance | undefined {\n if (typeof raw !== 'object' || raw === null) {\n return undefined;\n }\n const kept: AiProvenance = {};\n for (const key of ['model', 'promptHash', 'generatedAt'] as const) {\n const value = (raw as Record<string, unknown>)[key];\n if (typeof value === 'string' && value.length > 0 && value.length <= 200) {\n kept[key] = value;\n }\n }\n return Object.keys(kept).length > 0 ? kept : undefined;\n}\n\n/**\n * What the call cost, as a port reported it. Read on the same terms as the\n * provenance: a number that is not finite and zero or more says nothing about\n * a cost, so it is dropped rather than carried into a host's totals.\n */\nexport function usageOf(raw: unknown): GraderUsage | undefined {\n if (typeof raw !== 'object' || raw === null) {\n return undefined;\n }\n const kept: GraderUsage = {};\n for (const key of ['promptTokens', 'completionTokens', 'costUsd'] as const) {\n const value = (raw as Record<string, unknown>)[key];\n if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {\n kept[key] = value;\n }\n }\n return Object.keys(kept).length > 0 ? kept : undefined;\n}\n\n/** Typographic quotes a model writes for the plain ones a learner typed, and back. */\nconst QUOTE_FOLD: Readonly<Record<string, string>> = {\n '‘': \"'\",\n '’': \"'\",\n '‛': \"'\",\n '′': \"'\",\n '“': '\"',\n '”': '\"',\n '„': '\"',\n '″': '\"',\n};\n\n/**\n * Text as a quote is matched against it: typographic quotes folded to plain\n * ones and every run of whitespace one space — the two liberties a model takes\n * when it copies words out — with, for each folded character, where it came\n * from, so a match maps back to the learner's own text. Case is kept: a\n * correction of \"i\" to \"I\" is about case.\n */\nexport function folded(text: string): { chars: string; from: number[] } {\n let chars = '';\n const from: number[] = [];\n let space = false;\n for (let at = 0; at < text.length; at += 1) {\n const char = text[at] as string;\n if (/\\s/u.test(char)) {\n if (!space) {\n chars += ' ';\n from.push(at);\n space = true;\n }\n continue;\n }\n space = false;\n chars += QUOTE_FOLD[char] ?? char;\n from.push(at);\n }\n return { chars, from };\n}\n\n/** A quote as it is searched for: folded, and without the spaces around it. */\nexport const needle = (quote: string): string => folded(quote).chars.trim();\n","import { materialFacts } from './ai-material.js';\nimport { cleanText, provenanceOf, usageOf } from './ai-text.js';\nimport { numberedPassage, placeholderOrder, revealsAnswer } from './answer-leak.js';\nimport { everyBuiltInTypeHandled } from './built-in-types.js';\nimport { alignDictation } from './scoring/dictation/align.js';\nimport { isGradeInRange } from './scoring/grade-numbers.js';\nimport { score } from './scoring/index.js';\nimport type {\n ActivityData,\n DictationData,\n FillInTheBlanksData,\n GapSelectData,\n ItemOutcome,\n LearnerResponse,\n MultipleChoiceData,\n ScoringDetail,\n} from './types/activity.js';\nimport type {\n AiActivityInput,\n AiExplanationRequest,\n AiFeature,\n AiGrade,\n AiGroupInput,\n AiHintRequest,\n AiItemFacts,\n AiRefusal,\n AiSupportedActivityType,\n AiTextResult,\n AiVerdict,\n AiWordFact,\n} from './types/ai.js';\n\n/**\n * The activity types the SDK builds facts for, and which of them take hints.\n * A dictation takes none: its answer is the whole sentence, and it has\n * word-by-word hints of its own.\n */\nexport const AI_EXPLANATION_TYPES: readonly AiSupportedActivityType[] = [\n 'multiple-choice',\n 'fill-in-the-blanks',\n 'gap-select',\n 'dictation',\n];\nexport const AI_HINT_TYPES: readonly AiSupportedActivityType[] = [\n 'multiple-choice',\n 'fill-in-the-blanks',\n 'gap-select',\n];\n\n/** The longest AI text shown, in code points. Longer is refused, not cut. */\nexport const AI_TEXT_MAX_LENGTH = 2000;\n\nexport { AI_MATERIAL_MAX_LENGTH } from './ai-material.js';\n\n/**\n * Whether the SDK supports `feature` for this activity type: explanations and\n * hints for the types it builds facts for, and writing feedback for a written\n * response, and coaching for a read-aloud. A consumer's own type has none.\n */\nexport function aiSupports(activityType: string, feature: AiFeature): boolean {\n if (feature === 'writing-feedback') {\n return activityType === 'written-response';\n }\n if (feature === 'pronunciation-coaching') {\n return activityType === 'read-aloud';\n }\n const types = feature === 'hint' ? AI_HINT_TYPES : AI_EXPLANATION_TYPES;\n return (types as readonly string[]).includes(activityType);\n}\n\n/**\n * Whether the item's author allows `feature`. Only an explicit `false`\n * switches it off: an author can refuse a feature, never force one on.\n *\n * Writing feedback and pronunciation coaching read `explanations`: each is a\n * model's words about the learner's own answer, and an author who switched one\n * off has switched off the others.\n */\nexport function aiAllowedByContent(data: AiActivityInput, feature: AiFeature): boolean {\n const permissions = (data as { ai?: unknown }).ai;\n if (typeof permissions !== 'object' || permissions === null) {\n return true;\n }\n const key = feature === 'hint' ? 'hints' : 'explanations';\n return (permissions as Record<string, unknown>)[key] !== false;\n}\n\n/** A grade as an explanation must speak to it: all marks, some, or none. */\nexport function aiGradeOf(result: { score: number; maxScore: number; passed: boolean }): AiGrade {\n const ratio = result.maxScore > 0 ? result.score / result.maxScore : 0;\n let category: AiVerdict = 'partly-correct';\n if (ratio >= 1 - 1e-9) {\n category = 'correct';\n } else if (ratio <= 1e-9) {\n category = 'incorrect';\n }\n return { score: result.score, maxScore: result.maxScore, passed: result.passed, category };\n}\n\n// ── Facts ──────────────────────────────────────────────────────────────\n\n/**\n * The `correct` flag a detail stored before 0.3 carries instead of an\n * `outcome`. On a blank, a gap or a word it meant the part was right; on a\n * multiple-choice option, that the learner acted rightly on it.\n */\nconst legacyCorrect = (detail: ScoringDetail): boolean =>\n (detail as { correct?: unknown }).correct === true;\n\n/** Whether a blank, a gap or a word was right, read from a server's detail. */\nfunction detailRight(detail: ScoringDetail): boolean {\n const outcome: unknown = detail.outcome;\n return outcome === undefined ? legacyCorrect(detail) : outcome === 'correct';\n}\n\n/** Whether an option is part of the answer, read from a server's detail (as the renderer reads it). */\nfunction isAnswerOption(detail: ScoringDetail, chosen: boolean): boolean {\n switch (detail.outcome) {\n case 'correct':\n case 'incorrect-omission':\n return true;\n case 'incorrect':\n case 'correct-omission':\n return false;\n default:\n return chosen ? legacyCorrect(detail) : !legacyCorrect(detail);\n }\n}\n\nconst firstString = (value: string | string[] | undefined): string | undefined =>\n Array.isArray(value) ? value[0] : value;\n\nconst listOf = (value: string | string[] | undefined): string[] | null =>\n value === undefined ? null : Array.isArray(value) ? [...value] : [value];\n\n/** The response a learner has before touching anything. */\nfunction emptyResponse(type: string): LearnerResponse | null {\n switch (type) {\n case 'multiple-choice':\n return { type: 'multiple-choice', selectedOptionIds: [] };\n case 'fill-in-the-blanks':\n return { type: 'fill-in-the-blanks', answers: {} };\n case 'gap-select':\n return { type: 'gap-select', selections: {} };\n case 'dictation':\n return { type: 'dictation', text: '' };\n default:\n return null;\n }\n}\n\nconst base = (data: AiActivityInput) => ({\n activityId: data.id,\n title: data.title,\n ...(typeof data.locale === 'string' ? { locale: data.locale } : {}),\n});\n\n/** The item's instructions, trimmed; nothing for none, or for blank ones the learner never sees. */\nfunction instructionsOf(data: AiActivityInput): { instructions?: string } {\n const text: unknown = (data as { instructions?: unknown }).instructions;\n return typeof text === 'string' && text.trim() !== '' ? { instructions: text.trim() } : {};\n}\n\n/**\n * The facts a model is given about one item and one answer. `details` are the\n * SDK's per-part verdicts — from its own scorer, or from a server's outcome in\n * review — and every `correct` is `null` without them. A redacted item yields\n * facts without a key, which is what the learner's side holds; a port that\n * needs the key loads the item on its server.\n *\n * `null` for a type the SDK builds no facts for.\n */\nexport function buildAiFacts(\n data: AiActivityInput,\n response: LearnerResponse | null,\n details: readonly ScoringDetail[] | null,\n): AiItemFacts | null {\n const byId = new Map((details ?? []).map((detail) => [detail.itemId, detail]));\n const full = (data as { redacted?: unknown }).redacted !== true;\n switch (data.type) {\n case 'multiple-choice': {\n const item = data as unknown as MultipleChoiceData;\n const chosen = new Set(\n response?.type === 'multiple-choice' ? response.selectedOptionIds : [],\n );\n return {\n ...base(data),\n activityType: 'multiple-choice',\n ...instructionsOf(data),\n question: item.question,\n mode: item.mode,\n options: item.options.map((option) => {\n const detail = byId.get(option.id);\n const wasChosen = chosen.has(option.id);\n let correct: boolean | null = null;\n if (full && typeof option.isCorrect === 'boolean') {\n correct = option.isCorrect;\n } else if (detail !== undefined) {\n correct = isAnswerOption(detail, wasChosen);\n }\n return {\n id: option.id,\n text: option.text,\n chosen: wasChosen,\n correct,\n ...(typeof option.feedback === 'string' ? { feedback: option.feedback } : {}),\n };\n }),\n };\n }\n case 'fill-in-the-blanks': {\n const item = data as unknown as FillInTheBlanksData;\n const answers = response?.type === 'fill-in-the-blanks' ? response.answers : {};\n const blanks = new Map(item.blanks.map((blank) => [blank.id, blank]));\n return {\n ...base(data),\n activityType: 'fill-in-the-blanks',\n ...instructionsOf(data),\n passage: numberedPassage(item.passage),\n blanks: placeholderOrder(item.passage).map((id, index) => {\n const blank = blanks.get(id);\n const detail = byId.get(id);\n const accepted =\n full && Array.isArray(blank?.acceptedAnswers)\n ? [...blank.acceptedAnswers]\n : listOf(detail?.correctResponse);\n return {\n id,\n position: index + 1,\n typed: typeof answers[id] === 'string' ? (answers[id] as string) : '',\n accepted,\n correct: detail === undefined ? null : detailRight(detail),\n ...(typeof blank?.hint === 'string' ? { hint: blank.hint } : {}),\n ...(typeof blank?.feedback === 'string' ? { feedback: blank.feedback } : {}),\n };\n }),\n };\n }\n case 'gap-select': {\n const item = data as unknown as GapSelectData;\n const selections = response?.type === 'gap-select' ? response.selections : {};\n const gaps = new Map(item.gaps.map((gap) => [gap.id, gap]));\n const banks = new Map((item.banks ?? []).map((bank) => [bank.id, bank]));\n return {\n ...base(data),\n activityType: 'gap-select',\n ...instructionsOf(data),\n passage: numberedPassage(item.passage),\n gaps: placeholderOrder(item.passage).map((id, index) => {\n const gap = gaps.get(id);\n const choices =\n gap?.choices ??\n (gap?.bankId === undefined ? [] : (banks.get(gap.bankId)?.choices ?? []));\n const textOf = (choiceId: string | undefined): string | null =>\n choiceId === undefined\n ? null\n : (choices.find((choice) => choice.id === choiceId)?.text ?? null);\n const detail = byId.get(id);\n const answerId =\n full && typeof gap?.correctChoiceId === 'string'\n ? gap.correctChoiceId\n : firstString(detail?.correctResponse);\n return {\n id,\n position: index + 1,\n choices: choices.map((choice) => choice.text),\n chosen: textOf(selections[id]),\n answer: textOf(answerId),\n correct: detail === undefined ? null : detailRight(detail),\n ...(typeof gap?.feedback === 'string' ? { feedback: gap.feedback } : {}),\n };\n }),\n };\n }\n case 'dictation': {\n const item = data as unknown as DictationData;\n const typed = response?.type === 'dictation' ? response.text : '';\n const hasKey = full && typeof item.transcript === 'string';\n let words: AiWordFact[] | null = null;\n if (hasKey) {\n words = alignDictation(item, typed).words.map((word) => ({\n expected: word.reference,\n typed: word.attempt,\n status: word.status,\n }));\n } else if (details !== null && details.length > 0) {\n words = details.map((detail) => {\n const expected = firstString(detail.correctResponse) ?? '';\n const attempt = firstString(detail.learnerResponse) ?? '';\n let status: 'correct' | 'incorrect' | 'missing' | 'extra' = 'incorrect';\n if (detailRight(detail)) {\n status = 'correct';\n } else if (attempt === '') {\n status = 'missing';\n } else if (expected === '') {\n status = 'extra';\n }\n return { expected, typed: attempt, status };\n });\n }\n return {\n ...base(data),\n activityType: 'dictation',\n transcript: hasKey ? item.transcript : null,\n typed,\n words,\n };\n }\n default:\n return null;\n }\n}\n\n/** The SDK's own grade of `response`, with its per-part details; `null` when it cannot grade. */\nfunction gradeLocally(\n data: AiActivityInput,\n response: LearnerResponse,\n): { details: ScoringDetail[]; grade: AiGrade } | null {\n if ((data as { redacted?: unknown }).redacted === true) {\n return null;\n }\n try {\n const result = score(data.type as never, data as unknown as ActivityData, response);\n return { details: result.details, grade: aiGradeOf(result) };\n } catch {\n return null;\n }\n}\n\n/**\n * The request for an explanation of `response`, or `null` when there is none\n * to give: an unsupported type, an author who switched explanations off,\n * nothing graded to explain, or a grade of record that cannot be a grade.\n *\n * A scored `outcome` — the grade of record a review shows — wins over grading\n * locally, so the explanation speaks to the grade on the screen. One whose\n * numbers cannot be a grade (the rule `outcomeFromGrade` applies: `maxScore`\n * positive and finite, `score` finite from 0 to it) gets no explanation at all:\n * explaining 85 \"out of 1\" would tell the model a wrong answer was correct, and\n * grading locally instead would speak to a grade the screen does not show.\n *\n * `group` is the item group the question sits in: its material — the passage,\n * or the instructions the group's questions share — goes with the request as\n * `material`, as the learner sees it.\n */\nexport function aiExplanationRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n outcome?: ItemOutcome;\n group?: AiGroupInput;\n learnerLocale?: string;\n}): AiExplanationRequest | null {\n const { data, response, outcome, learnerLocale } = input;\n if (!aiSupports(data.type, 'explanation') || !aiAllowedByContent(data, 'explanation')) {\n return null;\n }\n let details: ScoringDetail[] | null = null;\n let grade: AiGrade | null = null;\n if (outcome?.status === 'scored') {\n // Read once, so the check and the grade handed on see the same numbers.\n const { score: recorded, maxScore, passed } = outcome;\n if (!isGradeInRange(recorded, maxScore)) {\n return null;\n }\n details = outcome.details;\n grade = aiGradeOf({ score: recorded, maxScore, passed });\n } else if (response !== null) {\n const local = gradeLocally(data, response);\n if (local !== null) {\n details = local.details;\n grade = local.grade;\n }\n }\n if (grade === null) {\n return null;\n }\n const facts = buildAiFacts(data, response, details);\n if (facts === null) {\n return null;\n }\n const material = materialFacts(input.group);\n return {\n feature: 'explanation',\n facts,\n grade,\n ...(material !== null ? { material } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n };\n}\n\n/**\n * The request for the next hint on an unsubmitted answer, or `null` when\n * there is none to give: an unsupported type, an author who switched hints\n * off, or a redacted item — hints are for practice, where the item is whole.\n *\n * The facts carry the key, and which parts the learner has right so far, so a\n * model can hint where it helps. The learner never sees them; a hint that\n * contains an answer is refused by {@link checkAiHint}, whatever it quotes —\n * the group's material included.\n *\n * `group` is the item group the question sits in, as for\n * {@link aiExplanationRequest}.\n */\nexport function aiHintRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n previousHints: readonly string[];\n group?: AiGroupInput;\n learnerLocale?: string;\n}): AiHintRequest | null {\n const { data, previousHints, learnerLocale } = input;\n if (\n !aiSupports(data.type, 'hint') ||\n !aiAllowedByContent(data, 'hint') ||\n (data as { redacted?: unknown }).redacted === true\n ) {\n return null;\n }\n const response = input.response ?? emptyResponse(data.type);\n const local = response === null ? null : gradeLocally(data, response);\n const facts = buildAiFacts(data, response, local?.details ?? null);\n if (facts === null) {\n return null;\n }\n const material = materialFacts(input.group);\n return {\n feature: 'hint',\n facts,\n hintNumber: previousHints.length + 1,\n previousHints: [...previousHints],\n ...(material !== null ? { material } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n };\n}\n\n// ── Checking what comes back ───────────────────────────────────────────\n\nconst VERDICTS: readonly string[] = ['correct', 'partly-correct', 'incorrect'];\n\n/**\n * Reads what a port returned. Anything but an object with a string `text` is\n * `malformed`; text that is empty once cleaned, or longer than\n * {@link AI_TEXT_MAX_LENGTH}, is refused rather than shown or cut.\n */\nexport function readAiTextResult(\n raw: unknown,\n): { ok: true; result: AiTextResult } | { ok: false; refusal: AiRefusal } {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n return { ok: false, refusal: 'malformed' };\n }\n const record = raw as Record<string, unknown>;\n if (typeof record.text !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n if (record.verdict !== undefined && !VERDICTS.includes(record.verdict as string)) {\n return { ok: false, refusal: 'malformed' };\n }\n const text = cleanText(record.text);\n if (text === '') {\n return { ok: false, refusal: 'empty' };\n }\n if ([...text].length > AI_TEXT_MAX_LENGTH) {\n return { ok: false, refusal: 'too-long' };\n }\n const provenance = provenanceOf(record.provenance);\n const usage = usageOf(record.usage);\n return {\n ok: true,\n result: {\n text,\n ...(record.verdict !== undefined ? { verdict: record.verdict as AiVerdict } : {}),\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n\n/**\n * Checks an explanation a port returned for `request`. On top of\n * {@link readAiTextResult}: an explanation that states a verdict other than\n * the SDK's grade is refused (`contradicts-grade`) — it explains an answer\n * the learner did not get.\n */\nexport function checkAiExplanation(\n raw: unknown,\n request: AiExplanationRequest,\n): { ok: true; result: AiTextResult } | { ok: false; refusal: AiRefusal } {\n const read = readAiTextResult(raw);\n if (!read.ok) {\n return read;\n }\n if (read.result.verdict !== undefined && read.result.verdict !== request.grade.category) {\n return { ok: false, refusal: 'contradicts-grade' };\n }\n return read;\n}\n\n/**\n * Checks a hint a port returned for `request`. On top of\n * {@link readAiTextResult}: a hint that contains an answer is refused\n * (`reveals-answer`) — see {@link hintRevealsAnswer}.\n */\nexport function checkAiHint(\n raw: unknown,\n request: AiHintRequest,\n): { ok: true; result: AiTextResult } | { ok: false; refusal: AiRefusal } {\n const read = readAiTextResult(raw);\n if (!read.ok) {\n return read;\n }\n if (hintRevealsAnswer(request.facts, read.result.text)) {\n return { ok: false, refusal: 'reveals-answer' };\n }\n return read;\n}\n\n// ── The answer-leak guard ──────────────────────────────────────────────\n\n/**\n * Whether a hint gives away an answer, judged against the facts it was asked\n * for. It is a floor, not a proof: it finds an answer written out, not one\n * spelled letter by letter or described.\n *\n * - **Multiple choice:** the text of any correct option, as whole words.\n * - **Fill-in-the-blanks and gap select:** any accepted answer, as whole words.\n * An answer of one short word (\"is\", \"the\") counts only where the hint\n * writes it beside a neighbour it has in the passage — \"name is\", \"is\n * Rossi\" — so a hint may still use the word on its own.\n * - **Facts of any other kind:** revealing. A hint the check cannot read is\n * withheld, not shown unchecked.\n *\n * Case, accents and punctuation are ignored on both sides.\n */\nexport function hintRevealsAnswer(facts: AiItemFacts, hint: string): boolean {\n const reveals = (answer: string, passage?: string, position?: number): boolean =>\n revealsAnswer(hint, answer, passage, position);\n switch (facts.activityType) {\n case 'multiple-choice':\n return facts.options.some((option) => option.correct === true && reveals(option.text));\n case 'fill-in-the-blanks':\n return facts.blanks.some((blank) =>\n (blank.accepted ?? []).some((answer) => reveals(answer, facts.passage, blank.position)),\n );\n case 'gap-select':\n return facts.gaps.some(\n (gap) => gap.answer !== null && reveals(gap.answer, facts.passage, gap.position),\n );\n case 'dictation':\n // Never asked: a dictation takes no AI hints (AI_HINT_TYPES).\n return false;\n default:\n // Facts of a kind this check cannot read: the hint is withheld, not shown\n // unchecked. A new kind of facts does not compile until it has a case.\n everyBuiltInTypeHandled(facts);\n return true;\n }\n}\n","import { aiAllowedByContent, aiSupports, readAiTextResult } from './ai.js';\nimport { cleanText } from './ai-text.js';\nimport { alignReadAloud } from './scoring/speech/align.js';\nimport { validateSpeechAssessment } from './scoring/speech/assessment.js';\nimport type { ReadAloudData } from './types/activity.js';\nimport type {\n AiActivityInput,\n AiCoaching,\n AiCoachingRequest,\n AiReadingFacts,\n AiReadingWordFact,\n AiRefusal,\n AiSoundFact,\n} from './types/ai.js';\nimport type { GradeRecord } from './types/grading.js';\nimport type { SpeechAssessment } from './types/speech.js';\n\n/** The most words one coaching may work on. More is refused, not cut. */\nexport const AI_COACHING_MAX_WORDS = 20;\n\n/** The longest tip for one word, in code points. */\nexport const AI_COACHING_MAX_TIP_LENGTH = 500;\n\n/** The longest sound symbol: a phoneme, not a sentence. */\nconst MAX_SYMBOL_LENGTH = 16;\n\n/** The dimensions a reading is scored on, as the facts name them. */\nconst DIMENSIONS = ['accuracy', 'fluency', 'completeness', 'prosody'] as const;\n\n/** Two decimals: what a stored score out of 1 was, out of 100, without float noise. */\nconst hundredths = (value: number): number => Math.round(value * 10000) / 100;\n\nconst isFiniteNumber = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value);\n\n/** A word's sounds as the engine reported them: only those it names. */\nfunction soundsOf(word: SpeechAssessment['words'][number] | undefined): AiSoundFact[] | undefined {\n const sounds: AiSoundFact[] = [];\n for (const phoneme of word?.phonemes ?? []) {\n if (typeof phoneme.symbol !== 'string') {\n continue;\n }\n sounds.push({\n symbol: phoneme.symbol,\n ...(phoneme.accuracy !== undefined ? { accuracy: phoneme.accuracy } : {}),\n ...(phoneme.heardAs !== undefined && phoneme.heardAs.length > 0\n ? { heardAs: phoneme.heardAs.map(({ symbol, score }) => ({ symbol, score })) }\n : {}),\n });\n }\n return sounds.length > 0 ? sounds : undefined;\n}\n\n/** The facts from the engine's full assessment: every word's mark, and its sounds. */\nfunction fromAssessment(\n item: ReadAloudData,\n assessment: SpeechAssessment,\n): Pick<AiReadingFacts, 'assessor' | 'phonemeAlphabet' | 'scores' | 'words'> {\n const words: AiReadingWordFact[] = alignReadAloud(item, assessment).map((entry) => {\n const sounds =\n entry.itemId !== undefined && entry.wordIndex !== undefined\n ? soundsOf(assessment.words[entry.wordIndex])\n : undefined;\n return {\n ...(entry.itemId !== undefined ? { itemId: entry.itemId } : {}),\n word: entry.reference,\n heard: entry.heard,\n state: entry.state,\n ...(entry.accuracy !== undefined ? { accuracy: entry.accuracy } : {}),\n ...(sounds !== undefined ? { sounds } : {}),\n };\n });\n const scores: AiReadingFacts['scores'] = {};\n for (const dimension of DIMENSIONS) {\n const value = assessment.scores[dimension];\n if (value !== undefined) {\n scores[dimension] = value;\n }\n }\n return {\n assessor: assessment.assessor.kind,\n ...(assessment.phonemeAlphabet !== undefined\n ? { phonemeAlphabet: assessment.phonemeAlphabet }\n : {}),\n scores,\n words,\n };\n}\n\n/**\n * Whether an assessment holds marks on this text: one `validateSpeechAssessment`\n * accepts, of speech heard, made against the item's own text in its own locale\n * — the checks `gradeReadAloud` makes before it reads a mark. Anything else is\n * not marks on this reading, and would tell a model every word was left out.\n */\nconst marksThisText = (item: ReadAloudData, assessment: SpeechAssessment): boolean =>\n validateSpeechAssessment(assessment).success &&\n assessment.status === 'assessed' &&\n assessment.task === 'scripted' &&\n assessment.locale === item.locale &&\n assessment.referenceText === item.referenceText;\n\nconst STATE_OF_OUTCOME = {\n correct: 'correct',\n incorrect: 'mispronounced',\n 'incorrect-omission': 'omitted',\n} as const;\n\n/**\n * The facts from a stored grade alone: each word's mark and score, and no\n * sounds — a grade's details do not keep them.\n */\nfunction fromGrade(\n grade: GradeRecord,\n): Pick<AiReadingFacts, 'assessor' | 'scores' | 'words'> | null {\n if (!Array.isArray(grade.details) || grade.details.length === 0) {\n return null;\n }\n const words: AiReadingWordFact[] = [];\n for (const detail of grade.details) {\n const state = STATE_OF_OUTCOME[detail.outcome as keyof typeof STATE_OF_OUTCOME];\n if (state === undefined || typeof detail.itemId !== 'string') {\n continue;\n }\n const text = (value: string | string[]): string =>\n Array.isArray(value) ? value.join(' ') : value;\n words.push({\n itemId: detail.itemId,\n word: text(detail.correctResponse),\n heard: text(detail.learnerResponse),\n state,\n ...(state !== 'omitted' && isFiniteNumber(detail.score)\n ? { accuracy: hundredths(detail.score) }\n : {}),\n });\n }\n if (words.length === 0) {\n return null;\n }\n // Each dimension out of 100, whatever scale the grade kept it on. A criterion\n // without a scale to read it on is left out rather than given one.\n const scores: AiReadingFacts['scores'] = {};\n for (const criterion of grade.criteria ?? []) {\n const dimension = DIMENSIONS.find((name) => name === criterion.name);\n if (\n dimension !== undefined &&\n isFiniteNumber(criterion.score) &&\n isFiniteNumber(criterion.maxScore) &&\n criterion.maxScore > 0\n ) {\n scores[dimension] = hundredths(criterion.score / criterion.maxScore);\n }\n }\n const kind = grade.grader?.kind;\n return {\n assessor: kind === 'auto' || kind === 'ai' || kind === 'human' ? kind : 'unknown',\n scores,\n words,\n };\n}\n\n/**\n * The request for coaching on a graded reading, or `null` when there is none\n * to give: not a read-aloud, an author who switched explanations off (coaching\n * reads `ai.explanations`), or no marks.\n *\n * The marks come from the `assessment` when one is given — with each word's\n * sounds and what each was heard as — and then only from it: an assessment\n * `gradeReadAloud` would not read (no speech heard, unscripted, or made against\n * another text or locale) gives none, rather than a stored grade's marks the\n * learner is not looking at. With no assessment, they come from\n * `grade.details`, without sounds.\n *\n * The facts are the text as the learner saw it, those marks word by word, and\n * the engine's dimension scores. The marks are the engine's; the model\n * explains them.\n */\nexport function aiCoachingRequest(input: {\n data: AiActivityInput;\n assessment?: SpeechAssessment | null;\n grade?: GradeRecord | null;\n learnerLocale?: string;\n}): AiCoachingRequest | null {\n const { data, assessment, grade, learnerLocale } = input;\n if (\n !aiSupports(data.type, 'pronunciation-coaching') ||\n !aiAllowedByContent(data, 'pronunciation-coaching')\n ) {\n return null;\n }\n const item = data as unknown as ReadAloudData;\n if (typeof item.referenceText !== 'string' || typeof item.locale !== 'string') {\n return null;\n }\n // An assessment given is the marks, and the only ones: a learner looking at\n // it is not looking at a stored grade's, and coaching must be about what is\n // on screen.\n const marks =\n assessment !== undefined && assessment !== null\n ? marksThisText(item, assessment)\n ? fromAssessment(item, assessment)\n : null\n : grade !== undefined && grade !== null\n ? fromGrade(grade)\n : null;\n if (marks === null) {\n return null;\n }\n const graded =\n grade !== undefined &&\n grade !== null &&\n isFiniteNumber(grade.score) &&\n isFiniteNumber(grade.maxScore) &&\n typeof grade.passed === 'boolean'\n ? { score: grade.score, maxScore: grade.maxScore, passed: grade.passed }\n : undefined;\n return {\n feature: 'pronunciation-coaching',\n facts: {\n activityType: 'read-aloud',\n activityId: data.id,\n title: data.title,\n locale: item.locale,\n referenceText: item.referenceText,\n ...(typeof item.instructions === 'string' ? { instructions: item.instructions } : {}),\n ...marks,\n },\n ...(graded !== undefined ? { grade: graded } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n };\n}\n\n// ── Checking what comes back ───────────────────────────────────────────\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/** A symbol as it is compared: composed, and without the spaces around it. */\nconst symbolKey = (symbol: string): string => symbol.normalize('NFC').trim();\n\ntype Refused = { ok: false; refusal: AiRefusal };\n\n/**\n * Checks coaching a port returned for `request`, and turns it into what a\n * learner is shown. As every AI text, it is shown whole or not at all.\n *\n * - **The text** is read as every AI text is (`readAiTextResult`).\n * - **Every word coached is one the engine marked.** Each `itemId` names a word\n * of the text marked `mispronounced` or `omitted`, once; coaching on a word\n * read correctly, or one the text does not have, refuses the whole reply as\n * `contradicts-marks`. Praise belongs in `text`.\n * - **Every sound named is one the engine reported** for that word, and a sound\n * it was heard as is one the engine heard it as. Where the facts carry no\n * sounds — marks from a stored grade, or an assessor that reports none — any\n * sound is `contradicts-marks`: nothing stands behind it.\n * - **Nothing numeric is read.** A score in the reply is ignored: the marks and\n * the grade are the engine's.\n * - **Limits:** {@link AI_COACHING_MAX_WORDS} words, a tip of\n * {@link AI_COACHING_MAX_TIP_LENGTH} code points; over them `too-long`. A\n * shape that is not the one documented is `malformed`.\n *\n * The words come back in reading order, each with the word as the marks show it\n * (the SDK's reading of the text, never the model's), and each sound spelt as\n * the engine spelt it.\n */\nexport function checkAiCoaching(\n raw: unknown,\n request: AiCoachingRequest,\n): { ok: true; coaching: AiCoaching } | { ok: false; refusal: AiRefusal } {\n const read = readAiTextResult(raw);\n if (!read.ok) {\n return read;\n }\n const entries = (raw as Record<string, unknown>).words;\n const words: (AiCoaching['words'][number] & { order: number })[] = [];\n if (entries !== undefined && entries !== null) {\n if (!Array.isArray(entries)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (entries.length > AI_COACHING_MAX_WORDS) {\n return { ok: false, refusal: 'too-long' };\n }\n const facts = new Map<string, { fact: AiReadingWordFact; order: number }>();\n request.facts.words.forEach((fact, order) => {\n if (fact.itemId !== undefined) {\n facts.set(fact.itemId, { fact, order });\n }\n });\n const seen = new Set<string>();\n for (const entry of entries) {\n if (!isRecord(entry) || typeof entry.itemId !== 'string' || typeof entry.tip !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n const tip = cleanText(entry.tip);\n if (tip === '') {\n return { ok: false, refusal: 'malformed' };\n }\n if ([...tip].length > AI_COACHING_MAX_TIP_LENGTH) {\n return { ok: false, refusal: 'too-long' };\n }\n if (seen.has(entry.itemId)) {\n return { ok: false, refusal: 'malformed' };\n }\n seen.add(entry.itemId);\n const found = facts.get(entry.itemId);\n if (\n found === undefined ||\n (found.fact.state !== 'mispronounced' && found.fact.state !== 'omitted')\n ) {\n return { ok: false, refusal: 'contradicts-marks' };\n }\n const sound = checkSound(entry.sound, found.fact);\n if (sound !== undefined && 'refusal' in sound) {\n return sound;\n }\n words.push({\n itemId: entry.itemId,\n word: found.fact.word,\n tip,\n ...(sound !== undefined ? { sound } : {}),\n order: found.order,\n });\n }\n }\n const { text, provenance, usage } = read.result;\n return {\n ok: true,\n coaching: {\n text,\n words: words.sort((a, b) => a.order - b.order).map(({ order: _order, ...word }) => word),\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n\n/** A sound a model names, checked against the sounds the engine reported for the word. */\nfunction checkSound(\n raw: unknown,\n fact: AiReadingWordFact,\n): { expected: string; heard?: string } | Refused | undefined {\n if (raw === undefined || raw === null) {\n return undefined;\n }\n if (\n !isRecord(raw) ||\n typeof raw.expected !== 'string' ||\n (raw.heard !== undefined && raw.heard !== null && typeof raw.heard !== 'string')\n ) {\n return { ok: false, refusal: 'malformed' };\n }\n const heard = typeof raw.heard === 'string' ? raw.heard : undefined;\n if (\n [...raw.expected].length > MAX_SYMBOL_LENGTH ||\n (heard !== undefined && [...heard].length > MAX_SYMBOL_LENGTH)\n ) {\n return { ok: false, refusal: 'too-long' };\n }\n const expected = symbolKey(raw.expected);\n const reported = fact.sounds?.find((sound) => symbolKey(sound.symbol) === expected);\n if (reported === undefined) {\n return { ok: false, refusal: 'contradicts-marks' };\n }\n if (heard === undefined) {\n return { expected: reported.symbol };\n }\n const instead = reported.heardAs?.find(\n (candidate) => symbolKey(candidate.symbol) === symbolKey(heard),\n );\n if (instead === undefined) {\n return { ok: false, refusal: 'contradicts-marks' };\n }\n return { expected: reported.symbol, heard: instead.symbol };\n}\n","import { getActivityTypeDescriptor, type MediaSlot } from './registry/index.js';\n\n/**\n * What a media reference points at. The first four are a holder's main file,\n * named by its `type`; the rest are the files beside it.\n */\nexport type MediaRefKind = 'audio' | 'video' | 'image' | 'embed' | 'captions' | 'track' | 'poster';\n\n/** One address in a piece of content, with where it sits. See {@link listMediaRefs}. */\nexport interface MediaRef {\n /**\n * From the entry to the field holding the address, e.g.\n * `['items', 2, 'options', 1, 'media', 'url']`. Authored positions, so a\n * shuffle never moves it.\n */\n readonly path: readonly (string | number)[];\n readonly kind: MediaRefKind;\n /**\n * The address. `null` for a holder's main file that has none yet: a draft\n * whose recording is still to be made, as a parallel item's is.\n */\n readonly url: string | null;\n /**\n * The object holding the address, as stored and never to be changed: the\n * media, an option's media, a slow recording, or a caption track. A\n * recording's `transcript` and `script` are read from it.\n */\n readonly holder: Readonly<Record<string, unknown>>;\n /** The activity it belongs to. Absent for a group's stimulus. */\n readonly activityId?: string;\n /** The stimulus it belongs to, for a group's material. */\n readonly stimulusId?: string;\n /** A `track`'s language. */\n readonly srclang?: string;\n}\n\nconst MAIN_KINDS: readonly string[] = ['audio', 'video', 'image', 'embed'];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction valueAt(root: unknown, path: readonly (string | number)[]): unknown {\n let at = root;\n for (const key of path) {\n if (typeof key === 'number' ? !Array.isArray(at) : !isRecord(at)) {\n return undefined;\n }\n at = (at as Record<string | number, unknown>)[key];\n }\n return at;\n}\n\n/** Where `item` keeps its files: what its type declares, or its `media` alone. */\nfunction slotsOf(item: Record<string, unknown>): readonly MediaSlot[] {\n const declared =\n typeof item.type === 'string' ? getActivityTypeDescriptor(item.type)?.mediaSlots : undefined;\n if (declared === undefined) {\n return [{ path: ['media'] }];\n }\n const slots = declared(item);\n return Array.isArray(slots) ? slots : [];\n}\n\ninterface Owner {\n activityId?: string;\n stimulusId?: string;\n}\n\nfunction holderRefs(\n holder: unknown,\n path: readonly (string | number)[],\n kindOfSlot: MediaSlot['kind'],\n owner: Owner,\n): MediaRef[] {\n if (!isRecord(holder)) {\n return [];\n }\n const own = typeof holder.type === 'string' && MAIN_KINDS.includes(holder.type);\n const main = (own ? holder.type : kindOfSlot) as MediaRefKind | undefined;\n const refs: MediaRef[] = [];\n const add = (field: readonly (string | number)[], kind: MediaRefKind, url: string | null) => {\n refs.push({ path: [...path, ...field], kind, url, holder, ...owner });\n };\n // A holder that names no kind of file is not valid content, and its file\n // could not be named; it is left out rather than guessed at.\n if (main !== undefined) {\n add(['url'], main, typeof holder.url === 'string' ? holder.url : null);\n }\n if (typeof holder.captionsUrl === 'string') {\n add(['captionsUrl'], 'captions', holder.captionsUrl);\n }\n if (Array.isArray(holder.tracks)) {\n holder.tracks.forEach((track, index) => {\n if (isRecord(track) && typeof track.src === 'string') {\n refs.push({\n path: [...path, 'tracks', index, 'src'],\n kind: 'track',\n url: track.src,\n holder: track,\n ...owner,\n ...(typeof track.srclang === 'string' ? { srclang: track.srclang } : {}),\n });\n }\n });\n }\n if (typeof holder.poster === 'string') {\n add(['poster'], 'poster', holder.poster);\n }\n return refs;\n}\n\nfunction activityRefs(item: unknown, base: readonly (string | number)[]): MediaRef[] {\n if (!isRecord(item)) {\n return [];\n }\n const owner: Owner = typeof item.id === 'string' ? { activityId: item.id } : {};\n return slotsOf(item).flatMap((slot) =>\n holderRefs(valueAt(item, slot.path), [...base, ...slot.path], slot.kind, owner),\n );\n}\n\n/**\n * Every media address in one piece of content: a loose activity or an item\n * group (its stimulus first, then each item in authored order), as a\n * `SequenceEntry` holds them. Drafts are read too.\n *\n * For each activity the walker reads the places its type keeps files: an\n * item's `media`, each multiple-choice option's `media`, and dictation's and\n * read-aloud's `slowMedia`. In each it lists the main file, `captionsUrl`,\n * every track's `src` and `poster`. A type you register declares its own with\n * `mediaSlots`; without that, its `media` is read.\n *\n * A main file with no address yet is listed with `url: null`, so a recording\n * still to be made has a place in the list. Not read: addresses inside an\n * `…Html` sidecar (the SDK does not parse HTML) and a learner's recordings,\n * which are responses rather than content.\n *\n * ```ts\n * const files = listMediaRefs(entry).filter((ref) => ref.url !== null);\n * ```\n */\nexport function listMediaRefs(entry: unknown): MediaRef[] {\n if (!isRecord(entry)) {\n return [];\n }\n if (entry.type !== 'item-group') {\n return activityRefs(entry, []);\n }\n const refs: MediaRef[] = [];\n const stimulus = entry.stimulus;\n if (isRecord(stimulus)) {\n const owner: Owner = typeof stimulus.id === 'string' ? { stimulusId: stimulus.id } : {};\n refs.push(...holderRefs(stimulus.media, ['stimulus', 'media'], undefined, owner));\n }\n if (Array.isArray(entry.items)) {\n entry.items.forEach((item, index) => {\n refs.push(...activityRefs(item, ['items', index]));\n });\n }\n return refs;\n}\n\n/** A deep copy of a JSON value. Content is plain data; a cycle cannot be copied. */\nfunction copyOf(value: unknown, seen: Set<object>): unknown {\n if (typeof value !== 'object' || value === null) {\n return value;\n }\n if (seen.has(value)) {\n throw new TypeError('mapMediaRefs: the content contains a circular reference.');\n }\n seen.add(value);\n try {\n if (Array.isArray(value)) {\n return value.map((element) => copyOf(element, seen));\n }\n const copy: Record<string, unknown> = {};\n for (const [key, field] of Object.entries(value)) {\n copy[key] = copyOf(field, seen);\n }\n return copy;\n } finally {\n seen.delete(value);\n }\n}\n\n/**\n * A copy of `entry` with each address {@link listMediaRefs} finds replaced\n * by what `fn` returns for it: files moved into a new folder structure, or an\n * export's file names pointed at where an import stored them. Nothing else\n * changes, and `entry` is never modified: with `(ref) => ref.url`, the copy is\n * deep-equal to it, with the same `canonicalJson`.\n *\n * For a main file with no address yet (`url: null`), a string attaches one\n * and `null` leaves it empty. For an address that is there, `fn` must return\n * a string: removing a file is not mapping.\n *\n * The copy validates as the original did when every address `fn` returns is\n * one the media rule accepts: `https:`, `http:`, `data:`, `blob:`, or\n * root-relative (`/…`). A bundle-relative `media/part1.mp3` is refused.\n *\n * @throws TypeError when `fn` returns something other than a string or\n * `null`, or `null` for an address that is there, or when `entry` is circular.\n */\nexport function mapMediaRefs<T>(entry: T, fn: (ref: MediaRef) => string | null): T {\n const refs = listMediaRefs(entry);\n const copy = copyOf(entry, new Set()) as T;\n for (const ref of refs) {\n const next = fn(ref);\n if (next === null) {\n if (ref.url !== null) {\n throw new TypeError(\n `mapMediaRefs: ${ref.path.join('.')} has a file; returning null would remove it, which is not mapping.`,\n );\n }\n continue;\n }\n if (typeof next !== 'string') {\n throw new TypeError(\n `mapMediaRefs: the function must return a string or null for ${ref.path.join('.')}, not ${typeof next}.`,\n );\n }\n if (next === ref.url) {\n continue;\n }\n const holder = valueAt(copy, ref.path.slice(0, -1)) as Record<string | number, unknown>;\n holder[ref.path[ref.path.length - 1] as string | number] = next;\n }\n return copy;\n}\n\n/** A holder's own text: its transcript, else its script as \"Speaker: text\" lines. */\nfunction ownText(holder: Record<string, unknown>): string | null {\n if (typeof holder.transcript === 'string' && holder.transcript.trim() !== '') {\n return holder.transcript;\n }\n const turns = Array.isArray(holder.script)\n ? holder.script.flatMap((turn) =>\n isRecord(turn) &&\n typeof turn.speaker === 'string' &&\n typeof turn.text === 'string' &&\n turn.text.trim() !== ''\n ? [`${turn.speaker.trim()}: ${turn.text.trim()}`]\n : [],\n )\n : [];\n return turns.length > 0 ? turns.join('\\n') : null;\n}\n\n/**\n * The author-side text of a recording: its `transcript`, or else its\n * `script` as one \"Speaker: text\" line per turn. `null` when it has neither.\n *\n * `holder` is a media object, a stimulus, or an activity. A stimulus or an\n * activity is read for its own text first and then for its `media`'s; a\n * stimulus keeps both at its own level. Author-only: pass it to the author's\n * requests (`aiDraftsRequest`'s `source`, a critique), never to a learner's.\n *\n * ```ts\n * const text = transcriptOf(item.media);\n * if (text !== null) aiDraftsRequest({ types: ['multiple-choice'], source: { kind: 'transcript', text } });\n * ```\n */\nexport function transcriptOf(holder: unknown): string | null {\n if (!isRecord(holder)) {\n return null;\n }\n return ownText(holder) ?? (isRecord(holder.media) ? ownText(holder.media) : null);\n}\n","import { fold } from '../answer-leak.js';\nimport { listMediaRefs } from '../media-refs.js';\nimport type { ItemFinding } from '../types/authoring.js';\nimport { finding } from './findings.js';\nimport { isRecord } from './issues.js';\n\n/**\n * The critic's rules for what a recording says: its author-only `transcript`\n * and `script`, wherever media sits. Words are compared as the other rules\n * compare them (`fold`: case, accents and punctuation ignored), and a\n * speaker's label is not one of a recording's words.\n */\n\ntype Path = readonly (string | number)[];\n\nconst written = (value: unknown): value is string =>\n typeof value === 'string' && value.trim() !== '';\n\n/** The script's turns that have a speaker and words, or none. */\nfunction turnsOf(script: unknown): { speaker: string; text: string }[] {\n return Array.isArray(script)\n ? script.flatMap((turn) =>\n isRecord(turn) && typeof turn.speaker === 'string' && written(turn.text)\n ? [{ speaker: turn.speaker, text: turn.text }]\n : [],\n )\n : [];\n}\n\nconst escaped = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n/** A transcript's words, with each `Speaker:` label of the script's speakers taken out. */\nfunction transcriptWords(transcript: string, speakers: readonly string[]): string[] {\n const labels = [...new Set(speakers.map((speaker) => speaker.trim()).filter(written))];\n const unlabelled =\n labels.length === 0\n ? transcript\n : transcript.replace(\n new RegExp(`(^|\\\\s)(?:${labels.map(escaped).join('|')})\\\\s*:`, 'giu'),\n '$1',\n );\n return fold(unlabelled);\n}\n\n/** What a recording says, as words: its transcript, or else its script's turns. */\nfunction spokenWords(holder: Record<string, unknown>): string[] | null {\n const turns = turnsOf(holder.script);\n if (written(holder.transcript)) {\n return transcriptWords(\n holder.transcript,\n turns.map((turn) => turn.speaker),\n );\n }\n return turns.length > 0 ? fold(turns.map((turn) => turn.text).join(' ')) : null;\n}\n\nconst sameWords = (a: readonly string[], b: readonly string[]) =>\n a.length === b.length && a.every((word, index) => word === b[index]);\n\n/** One object's own rules: text on media with nothing to say, and a transcript its script contradicts. */\nfunction holderFindings(holder: unknown, path: Path): ItemFinding[] {\n if (!isRecord(holder)) {\n return [];\n }\n const findings: ItemFinding[] = [];\n const turns = turnsOf(holder.script);\n const hasText = written(holder.transcript) || turns.length > 0;\n if (hasText && (holder.type === 'image' || holder.type === 'embed')) {\n findings.push(\n finding(\n 'media_transcript_unused',\n [...path, written(holder.transcript) ? 'transcript' : 'script'],\n `A transcript or a script on ${holder.type === 'image' ? 'a picture' : 'an embedded player'} has nothing to describe, and nothing reads it. Put it on the recording it belongs to.`,\n ),\n );\n }\n if (written(holder.transcript) && turns.length > 0) {\n const fromTranscript = transcriptWords(\n holder.transcript,\n turns.map((turn) => turn.speaker),\n );\n if (!sameWords(fromTranscript, fold(turns.map((turn) => turn.text).join(' ')))) {\n findings.push(\n finding(\n 'media_script_differs',\n [...path, 'script'],\n 'The script and the transcript say different words. AI facts read the transcript, and a voice reads the script: make them say the same.',\n ),\n );\n }\n }\n return findings;\n}\n\n/** The field a type's own recording reads out, for the types that have one. */\nconst SPOKEN_FIELD: Readonly<Record<string, string>> = {\n dictation: 'transcript',\n 'read-aloud': 'referenceText',\n};\n\n/**\n * An item's recording rules, at every place its type keeps media. A\n * dictation's and a read-aloud's own text is what their recording says, so a\n * recording that says other words is reported too.\n */\nexport function recordingFindings(item: Record<string, unknown>): ItemFinding[] {\n // Each media object once: the walker lists its main file first, at `<path>.url`.\n const findings = listMediaRefs(item)\n .filter((ref) => ref.path[ref.path.length - 1] === 'url')\n .flatMap((ref) => holderFindings(ref.holder, ref.path.slice(0, -1)));\n const field = typeof item.type === 'string' ? SPOKEN_FIELD[item.type] : undefined;\n const media = item.media;\n if (field === undefined || !isRecord(media)) {\n return findings;\n }\n const said = spokenWords(media);\n const accepted = [\n item[field],\n ...(item.type === 'dictation' && Array.isArray(item.acceptedTranscripts)\n ? item.acceptedTranscripts\n : []),\n ].filter(written);\n if (\n said !== null &&\n accepted.length > 0 &&\n !accepted.some((text) => sameWords(said, fold(text)))\n ) {\n findings.push(\n finding(\n 'media_transcript_differs',\n ['media', written(media.transcript) ? 'transcript' : 'script'],\n `The recording's transcript says different words from the item's \\`${field}\\`, which is what the recording is meant to say.`,\n ),\n );\n }\n return findings;\n}\n\n/** A group's material: its own transcript against its script, and its media's rules. */\nexport function stimulusFindings(stimulus: unknown): ItemFinding[] {\n if (!isRecord(stimulus)) {\n return [];\n }\n return [\n ...holderFindings({ transcript: stimulus.transcript, script: stimulus.script }, ['stimulus']),\n ...holderFindings(stimulus.media, ['stimulus', 'media']),\n ];\n}\n","import { fold, passageWithoutPlaceholders, placeholderOrder } from './answer-leak.js';\nimport { canonicalJson } from './content-hash.js';\nimport { policyFor } from './scoring/activity-scorers/fill-in-the-blanks.js';\nimport type { BlankConfig } from './types/activity.js';\n\n/**\n * What makes two items parallel, as rules: their structure, the length of what\n * a learner reads or hears, whether one copies the other, and whether they\n * share a right answer. One module, read by the variants a model writes\n * (`checkAiVariants`), by the paper critic (`critiqueDrafts`) and by the\n * comparison of two forms (`critiqueVersions`), so the three never disagree.\n *\n * Every rule reads content as stored, finished or not: it looks only at the\n * fields that are there and never throws.\n */\n\ntype Fields = Readonly<Record<string, unknown>>;\n\nconst isRecord = (value: unknown): value is Fields =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\nconst listOf = (value: unknown): readonly unknown[] => (Array.isArray(value) ? value : []);\nconst stringOf = (value: unknown): string => (typeof value === 'string' ? value : '');\nconst same = (a: unknown, b: unknown): boolean =>\n canonicalJson(a ?? null) === canonicalJson(b ?? null);\n\n/**\n * A passage's blanks or gaps in the order its placeholders appear, each with\n * its index in the list: the order a learner meets them, which is how two\n * items are compared and how a model writes them. The list's own order where\n * the passage does not name each of them once.\n */\nexport function inPassageOrder(\n item: Fields,\n field: 'blanks' | 'gaps',\n): { at: number; value: unknown }[] {\n const list = listOf(item[field]);\n const order = typeof item.passage === 'string' ? placeholderOrder(item.passage) : [];\n const found = order.map((id) => list.findIndex((one) => isRecord(one) && one.id === id));\n if (\n found.length === list.length &&\n found.every((at) => at !== -1) &&\n new Set(found).size === found.length\n ) {\n return found.map((at) => ({ at, value: list[at] }));\n }\n return list.map((value, at) => ({ at, value }));\n}\n\n// ── Similarity ─────────────────────────────────────────────────────────\n\n/**\n * How alike two texts must be to count as one copying the other: the share of\n * their word triples (3-word shingles) they have in common, as Jaccard\n * similarity. 0.5 means half of everything either text says, three words at a\n * time, is in the other.\n */\nexport const AI_VARIANT_NEAR_COPY_THRESHOLD = 0.5;\n\n/** Texts shorter than this, in words, are compared whole: word triples say nothing about them. */\nconst SHORT_TEXT_WORDS = 6;\n\n/**\n * The words of a text as every comparison here reads them: placeholders\n * (`{{1}}`) taken out, compatibility forms read as what they stand for, accents\n * and case ignored, and punctuation — typographic quotes included — gone.\n */\nexport function wordsOf(text: string): string[] {\n return fold(passageWithoutPlaceholders(text));\n}\n\nfunction triples(words: readonly string[]): Set<string> {\n const found = new Set<string>();\n for (let at = 0; at + 3 <= words.length; at += 1) {\n found.add(`${words[at]} ${words[at + 1]} ${words[at + 2]}`);\n }\n return found;\n}\n\n/**\n * How alike two texts are, from 0 to 1: the Jaccard similarity of their word\n * triples, or — when either has fewer than six words — 1 if they read the same\n * and 0 if not. A text with no words is like nothing.\n */\nexport function textSimilarity(a: string, b: string): number {\n const left = wordsOf(a);\n const right = wordsOf(b);\n if (left.length === 0 || right.length === 0) {\n return 0;\n }\n if (left.length < SHORT_TEXT_WORDS || right.length < SHORT_TEXT_WORDS) {\n return left.join(' ') === right.join(' ') ? 1 : 0;\n }\n const one = triples(left);\n const two = triples(right);\n let shared = 0;\n for (const triple of one) {\n if (two.has(triple)) {\n shared += 1;\n }\n }\n return shared / (one.size + two.size - shared);\n}\n\n/** A similarity as a message shows it: two decimals. */\nexport const shownScore = (score: number): string => score.toFixed(2);\n\n// ── The texts of an item ───────────────────────────────────────────────\n\n/**\n * What a text is, for comparing it with another: a question's own words\n * (`stem`: a question, a passage with gaps, a writing task), words a learner\n * hears or says (`spoken`: a dictation's transcript, a read-aloud's text), or\n * the material questions are about (`material`: a passage, a listening\n * script, what a recording says). Texts are compared with texts of their kind.\n */\nexport type TextKind = 'stem' | 'spoken' | 'material';\n\nexport interface ItemText {\n /** Where the text is, from the entry. */\n path: string[];\n kind: TextKind;\n /** Which length band applies: the material's, or an item text's. */\n band: 'material' | 'item';\n text: string;\n}\n\nconst RECORDING_TYPES: readonly string[] = ['audio', 'video', 'embed'];\n\n/** A recording's words: its script's turns, speakers left out, else its transcript. */\nfunction recordingText(holder: Fields): { field: 'script' | 'transcript'; text: string } | null {\n const turns = listOf(holder.script).flatMap((turn) =>\n isRecord(turn) && typeof turn.text === 'string' && turn.text.trim() !== '' ? [turn.text] : [],\n );\n if (turns.length > 0) {\n return { field: 'script', text: turns.join('\\n') };\n }\n return typeof holder.transcript === 'string' && holder.transcript.trim() !== ''\n ? { field: 'transcript', text: holder.transcript }\n : null;\n}\n\nfunction mediaTexts(media: unknown, at: readonly string[]): ItemText[] {\n if (!isRecord(media) || !RECORDING_TYPES.includes(stringOf(media.type))) {\n return [];\n }\n const said = recordingText(media);\n return said === null\n ? []\n : [{ path: [...at, said.field], kind: 'material', band: 'material', text: said.text }];\n}\n\n/** The fields an activity type keeps its own text in, and what each is. */\nconst OWN_TEXT: Readonly<Record<string, { field: string; kind: TextKind }>> = {\n 'multiple-choice': { field: 'question', kind: 'stem' },\n 'fill-in-the-blanks': { field: 'passage', kind: 'stem' },\n 'gap-select': { field: 'passage', kind: 'stem' },\n 'written-response': { field: 'prompt', kind: 'stem' },\n dictation: { field: 'transcript', kind: 'spoken' },\n 'read-aloud': { field: 'referenceText', kind: 'spoken' },\n};\n\n/**\n * Every text of an entry a near copy or a length is judged on: an item's own\n * text and what its recording says; for an item group, its stimulus's passage\n * and recording, then each item's under `items.N`. A dictation's or a\n * read-aloud's recording says the item's own text, so it is not read twice.\n */\nexport function textsOf(entry: unknown): ItemText[] {\n if (!isRecord(entry)) {\n return [];\n }\n if (entry.type === 'item-group') {\n const stimulus = isRecord(entry.stimulus) ? entry.stimulus : {};\n const own = recordingText(stimulus);\n return [\n ...(typeof stimulus.body === 'string' && stimulus.body.trim() !== ''\n ? [\n {\n path: ['stimulus', 'body'],\n kind: 'material' as const,\n band: 'material' as const,\n text: stimulus.body,\n },\n ]\n : []),\n ...(own !== null\n ? [\n {\n path: ['stimulus', own.field],\n kind: 'material' as const,\n band: 'material' as const,\n text: own.text,\n },\n ]\n : mediaTexts(stimulus.media, ['stimulus', 'media'])),\n ...listOf(entry.items).flatMap((item, index) =>\n textsOf(item).map((found) => ({ ...found, path: ['items', String(index), ...found.path] })),\n ),\n ];\n }\n const own = OWN_TEXT[stringOf(entry.type)];\n if (own === undefined) {\n return [];\n }\n const text = entry[own.field];\n return [\n ...(typeof text === 'string' && text.trim() !== ''\n ? [{ path: [own.field], kind: own.kind, band: 'item' as const, text }]\n : []),\n ...(own.kind === 'spoken' ? [] : mediaTexts(entry.media, ['media'])),\n ];\n}\n\n// ── Keys ───────────────────────────────────────────────────────────────\n\n/** A key as compared: folded, and kept in its case when its blank matches case. */\nexport function keyOf(text: string, caseSensitive = false): string {\n if (!caseSensitive) {\n return fold(text).join(' ');\n }\n return text\n .normalize('NFKD')\n .replace(/\\p{M}/gu, '')\n .replace(/[^\\p{L}\\p{N}]+/gu, ' ')\n .trim();\n}\n\n/**\n * One place an item keeps a right answer: a multiple choice's correct options\n * (`options`), a blank (`blanks.N`) or a gap (`gaps.N`). `answers` are the\n * keys as compared, `shown` the first as written.\n */\nexport interface KeySlot {\n slot: string;\n path: string[];\n answers: string[];\n shown: string;\n}\n\nfunction choiceText(item: Fields, gap: Fields): string | undefined {\n const own = listOf(gap.choices);\n const bank = listOf(item.banks).find((one) => isRecord(one) && one.id === gap.bankId);\n const choices = own.length > 0 ? own : isRecord(bank) ? listOf(bank.choices) : [];\n const right = choices.find((choice) => isRecord(choice) && choice.id === gap.correctChoiceId);\n return isRecord(right) && typeof right.text === 'string' ? right.text : undefined;\n}\n\n/** The right answers of one item (not a group), slot by slot. */\nexport function keysOf(item: unknown): KeySlot[] {\n if (!isRecord(item)) {\n return [];\n }\n const slots: KeySlot[] = [];\n const add = (slot: string, path: string[], written: readonly string[], caseSensitive = false) => {\n const answers = [...new Set(written.map((one) => keyOf(one, caseSensitive)))].filter(\n (one) => one !== '',\n );\n if (answers.length > 0) {\n slots.push({ slot, path, answers, shown: (written[0] as string).trim() });\n }\n };\n if (item.type === 'multiple-choice') {\n const options = listOf(item.options);\n const first = options.findIndex((option) => isRecord(option) && option.isCorrect === true);\n add(\n 'options',\n ['options', String(first), 'text'],\n options.flatMap((option) =>\n isRecord(option) && option.isCorrect === true && typeof option.text === 'string'\n ? [option.text]\n : [],\n ),\n );\n } else if (item.type === 'fill-in-the-blanks') {\n inPassageOrder(item, 'blanks').forEach(({ at, value: blank }, position) => {\n if (isRecord(blank)) {\n add(\n `blanks.${position}`,\n ['blanks', String(at), 'acceptedAnswers'],\n listOf(blank.acceptedAnswers).filter((one): one is string => typeof one === 'string'),\n policyFor(blank as unknown as BlankConfig).caseSensitive === true,\n );\n }\n });\n } else if (item.type === 'gap-select') {\n inPassageOrder(item, 'gaps').forEach(({ at, value: gap }, position) => {\n const text = isRecord(gap) ? choiceText(item, gap) : undefined;\n if (text !== undefined) {\n add(`gaps.${position}`, ['gaps', String(at), 'correctChoiceId'], [text]);\n }\n });\n }\n return slots;\n}\n\n/** The answers two key slots share, as the first slot writes them. */\nexport function sharedAnswers(a: KeySlot, b: KeySlot): string[] {\n return a.answers.filter((answer) => b.answers.includes(answer));\n}\n\n/** A multiple choice's options as a set of texts, for \"the same options in another order\". */\nexport function optionSet(item: unknown): string[] | null {\n if (!isRecord(item) || item.type !== 'multiple-choice') {\n return null;\n }\n const texts = listOf(item.options).map((option) =>\n isRecord(option) ? fold(stringOf(option.text)).join(' ') : '',\n );\n return texts.length === 0 || texts.some((one) => one === '') ? null : [...texts].sort();\n}\n\n// ── Length ─────────────────────────────────────────────────────────────\n\n/**\n * How far a parallel text may stray from its exemplar's length, in words:\n * `ratio` of the exemplar's words either way, and never less than `minWords`.\n * Material — a passage, a listening script, what a recording says — is held\n * closer than an item's own text, because how much a candidate reads or hears\n * is what makes one form of a part harder than another.\n */\nexport const AI_VARIANT_LENGTH_BANDS = {\n material: { ratio: 0.1, minWords: 5 },\n item: { ratio: 0.25, minWords: 2 },\n} as const;\n\n/** A host's own ratios, overriding the bands above: `{ material: 0.15 }`. */\nexport interface LengthTolerance {\n material?: number;\n item?: number;\n}\n\n/** Inclusive bounds, in words, for a text the exemplar writes in `words` words. */\nexport function lengthBand(\n words: number,\n band: 'material' | 'item',\n tolerance: LengthTolerance = {},\n): [number, number] {\n const rule = AI_VARIANT_LENGTH_BANDS[band];\n const asked = tolerance[band];\n const ratio =\n typeof asked === 'number' && Number.isFinite(asked) && asked >= 0 ? asked : rule.ratio;\n const leeway = Math.max(words * ratio, rule.minWords);\n return [Math.max(0, Math.ceil(words - leeway - 1e-9)), Math.floor(words + leeway + 1e-9)];\n}\n\n/** Every text of an entry with its band: path, kind and bounds. */\nexport function lengthTargets(\n entry: unknown,\n tolerance?: LengthTolerance,\n): { path: string[]; band: 'material' | 'item'; words: number; bounds: [number, number] }[] {\n return textsOf(entry).map((found) => {\n const words = wordsOf(found.text).length;\n return {\n path: found.path,\n band: found.band,\n words,\n bounds: lengthBand(words, found.band, tolerance),\n };\n });\n}\n\nexport interface LengthMiss {\n path: string[];\n band: 'material' | 'item';\n words: number;\n bounds: [number, number];\n exemplarWords: number;\n}\n\n/** The texts of `candidate` whose length falls outside the band of the same text in `exemplar`. */\nexport function lengthMisses(\n exemplar: unknown,\n candidate: unknown,\n tolerance?: LengthTolerance,\n): LengthMiss[] {\n const theirs = new Map(textsOf(candidate).map((found) => [found.path.join('.'), found]));\n return lengthTargets(exemplar, tolerance).flatMap((target) => {\n const found = theirs.get(target.path.join('.'));\n if (found === undefined) {\n return [];\n }\n const words = wordsOf(found.text).length;\n return words < target.bounds[0] || words > target.bounds[1]\n ? [\n {\n path: target.path,\n band: target.band,\n words,\n bounds: target.bounds,\n exemplarWords: target.words,\n },\n ]\n : [];\n });\n}\n\n// ── Structure ──────────────────────────────────────────────────────────\n\n/** One way a candidate's structure differs from its exemplar's. */\nexport interface StructureMiss {\n path: string[];\n message: string;\n}\n\nconst count = (n: number, what: string): string => `${n} ${what}${n === 1 ? '' : 's'}`;\n\n/** A script's speakers, by name, and its turns. */\nfunction scriptShape(holder: Fields): { speakers: number; turns: number } | null {\n const turns = listOf(holder.script).filter(isRecord);\n if (turns.length === 0) {\n return null;\n }\n return {\n speakers: new Set(turns.map((turn) => stringOf(turn.speaker).trim().toLowerCase())).size,\n turns: turns.length,\n };\n}\n\n/** The script a stimulus carries, on itself or on its media. */\nfunction stimulusScript(stimulus: Fields): { speakers: number; turns: number } | null {\n return scriptShape(stimulus) ?? (isRecord(stimulus.media) ? scriptShape(stimulus.media) : null);\n}\n\n/** How many turns a script of `turns` turns may have in a variant: within a quarter, at least one either way. */\nexport function turnBand(turns: number): [number, number] {\n const leeway = Math.max(turns * 0.25, 1);\n return [Math.max(1, Math.ceil(turns - leeway - 1e-9)), Math.floor(turns + leeway + 1e-9)];\n}\n\nconst mediaKind = (media: unknown): string | null =>\n isRecord(media) && typeof media.type === 'string' ? media.type : null;\n\nfunction isOneWord(answer: string): boolean {\n const trimmed = answer.trim();\n return trimmed !== '' && !/\\s/.test(trimmed);\n}\n\n/**\n * Which bank each gap draws on, by the bank's position, or `null` for a gap\n * with its own choices: in passage order.\n */\nfunction gapBanks(item: Fields): (number | null)[] {\n const banks = listOf(item.banks);\n return inPassageOrder(item, 'gaps').map(({ value: gap }) => {\n if (!isRecord(gap) || listOf(gap.choices).length > 0) {\n return null;\n }\n const at = banks.findIndex((bank) => isRecord(bank) && bank.id === gap.bankId);\n return at === -1 ? null : at;\n });\n}\n\n/**\n * The structure a variant must keep, as plain facts a model can be told: the\n * counts it must hit. Read from the exemplar; nothing here is checked.\n */\nexport function structureTargets(entry: unknown): Record<string, unknown> {\n if (!isRecord(entry)) {\n return {};\n }\n switch (entry.type) {\n case 'item-group': {\n const stimulus = isRecord(entry.stimulus) ? entry.stimulus : {};\n const script = stimulusScript(stimulus);\n return {\n stimulus: stringOf(stimulus.kind),\n items: listOf(entry.items).map((item) => ({\n type: isRecord(item) ? stringOf(item.type) : '',\n ...structureTargets(item),\n })),\n ...(script !== null ? { speakers: script.speakers, turns: turnBand(script.turns) } : {}),\n };\n }\n case 'multiple-choice': {\n const options = listOf(entry.options);\n const media = options.map((option) => (isRecord(option) ? mediaKind(option.media) : null));\n return {\n mode: stringOf(entry.mode),\n options: options.length,\n correct: options.filter((option) => isRecord(option) && option.isCorrect === true).length,\n ...(media.some((one) => one !== null) ? { optionMedia: media } : {}),\n };\n }\n case 'fill-in-the-blanks':\n return {\n blanks: inPassageOrder(entry, 'blanks').map(({ value: blank }) => {\n const answers = isRecord(blank)\n ? listOf(blank.acceptedAnswers).filter((one): one is string => typeof one === 'string')\n : [];\n return {\n oneWord: answers.length > 0 && answers.every(isOneWord),\n acceptedAnswers: [Math.max(1, answers.length - 2), answers.length + 2],\n };\n }),\n };\n case 'gap-select': {\n const banks = listOf(entry.banks);\n const drawn = gapBanks(entry);\n return {\n gaps: inPassageOrder(entry, 'gaps').map(({ value: gap }, index) =>\n drawn[index] !== null\n ? { bank: drawn[index] }\n : { choices: isRecord(gap) ? listOf(gap.choices).length : 0 },\n ),\n ...(banks.length > 0\n ? {\n banks: banks.map((bank) => ({\n choices: isRecord(bank) ? listOf(bank.choices).length : 0,\n })),\n }\n : {}),\n };\n }\n case 'written-response':\n return {\n minWords: entry.minWords,\n maxWords: entry.maxWords,\n ...(isRecord(entry.rubric)\n ? {\n criteria: listOf(entry.rubric.criteria).map((criterion) =>\n isRecord(criterion) ? stringOf(criterion.name) : '',\n ),\n }\n : {}),\n };\n default:\n return {};\n }\n}\n\n/** The settings two items must share to be scored alike, per type. */\nconst SCORED_ALIKE: Readonly<Record<string, readonly string[]>> = {\n 'multiple-choice': ['mode', 'scoringStrategy'],\n 'fill-in-the-blanks': ['scoringStrategy'],\n 'gap-select': ['scoringStrategy', 'presentation'],\n dictation: ['tolerance', 'hints'],\n 'read-aloud': ['recording', 'scoring', 'locale'],\n 'written-response': ['rubric', 'minWords', 'maxWords', 'languageTarget'],\n};\n\nconst DIFFERS: Readonly<Record<string, string>> = {\n mode: 'The mode differs',\n scoringStrategy: 'The scoring strategy differs',\n presentation: 'The presentation differs',\n tolerance: 'The tolerance differs',\n hints: 'The hints differ',\n recording: 'The recording bounds differ',\n scoring: 'The scored dimensions differ',\n locale: 'The locale differs',\n rubric: 'The rubric differs',\n minWords: 'The minimum word count differs',\n maxWords: 'The maximum word count differs',\n languageTarget: 'The language target differs',\n};\n\nfunction itemMisses(exemplar: Fields, candidate: Fields): StructureMiss[] {\n const misses: StructureMiss[] = [];\n const miss = (path: (string | number)[], message: string) =>\n misses.push({ path: path.map(String), message });\n const type = stringOf(exemplar.type);\n\n for (const field of SCORED_ALIKE[type] ?? []) {\n if (!same(exemplar[field], candidate[field])) {\n miss([field], `${DIFFERS[field]} from the exemplar's.`);\n }\n }\n if ((exemplar.shuffle === true) !== (candidate.shuffle === true)) {\n miss(['shuffle'], 'Shuffling differs from the exemplar.');\n }\n if (mediaKind(exemplar.media) !== mediaKind(candidate.media)) {\n miss(\n ['media'],\n `The exemplar has ${mediaKind(exemplar.media) ?? 'no'} media; this has ${mediaKind(candidate.media) ?? 'none'}.`,\n );\n }\n if (type === 'dictation' || type === 'read-aloud') {\n if (isRecord(exemplar.slowMedia) !== isRecord(candidate.slowMedia)) {\n miss(\n ['slowMedia'],\n isRecord(exemplar.slowMedia)\n ? 'The exemplar has a slow recording; this has none.'\n : 'This has a slow recording; the exemplar has none.',\n );\n }\n }\n\n if (type === 'multiple-choice') {\n const mine = listOf(exemplar.options);\n const theirs = listOf(candidate.options);\n if (mine.length !== theirs.length) {\n miss(['options'], `${count(theirs.length, 'option')}; the exemplar has ${mine.length}.`);\n }\n const right = (list: readonly unknown[]) =>\n list.filter((option) => isRecord(option) && option.isCorrect === true).length;\n if (right(mine) !== right(theirs)) {\n miss(\n ['options'],\n `${count(right(theirs), 'correct option')}; the exemplar has ${right(mine)}.`,\n );\n }\n const media = (list: readonly unknown[]) =>\n list\n .map((option) => (isRecord(option) ? mediaKind(option.media) : null))\n .filter((one) => one !== null)\n .sort()\n .join(',');\n if (media(mine) !== media(theirs)) {\n miss(['options'], \"The options with media differ from the exemplar's.\");\n }\n } else if (type === 'fill-in-the-blanks') {\n const mine = inPassageOrder(exemplar, 'blanks');\n const theirs = inPassageOrder(candidate, 'blanks');\n if (mine.length !== theirs.length) {\n miss(['blanks'], `${count(theirs.length, 'blank')}; the exemplar has ${mine.length}.`);\n }\n mine.forEach(({ value: blank }, index) => {\n const at = theirs[index]?.at;\n const other = theirs[index]?.value;\n if (!isRecord(blank) || !isRecord(other)) {\n return;\n }\n const policy = (one: Fields) => ({\n match: one.match,\n caseSensitive: one.caseSensitive,\n trimWhitespace: one.trimWhitespace,\n });\n if (!same(policy(blank), policy(other))) {\n miss(\n ['blanks', at as number],\n `Blank ${index + 1} matches answers differently from the exemplar's.`,\n );\n }\n const answers = (one: Fields) =>\n listOf(one.acceptedAnswers).filter(\n (answer): answer is string => typeof answer === 'string',\n );\n const exemplarAnswers = answers(blank);\n const candidateAnswers = answers(other);\n if (\n exemplarAnswers.length > 0 &&\n exemplarAnswers.every(isOneWord) &&\n !candidateAnswers.every(isOneWord)\n ) {\n miss(\n ['blanks', at as number, 'acceptedAnswers'],\n `Blank ${index + 1} takes one word in the exemplar; every answer here must be one word.`,\n );\n }\n if (Math.abs(candidateAnswers.length - exemplarAnswers.length) > 2) {\n miss(\n ['blanks', at as number, 'acceptedAnswers'],\n `Blank ${index + 1} accepts ${count(candidateAnswers.length, 'answer')}; the exemplar accepts ${exemplarAnswers.length}, and two more or fewer is the most.`,\n );\n }\n });\n } else if (type === 'gap-select') {\n const mine = inPassageOrder(exemplar, 'gaps');\n const theirs = inPassageOrder(candidate, 'gaps');\n if (mine.length !== theirs.length) {\n miss(['gaps'], `${count(theirs.length, 'gap')}; the exemplar has ${mine.length}.`);\n }\n const myBanks = listOf(exemplar.banks);\n const theirBanks = listOf(candidate.banks);\n if (myBanks.length !== theirBanks.length) {\n miss(['banks'], `${count(theirBanks.length, 'bank')}; the exemplar has ${myBanks.length}.`);\n }\n myBanks.forEach((bank, index) => {\n const other = theirBanks[index];\n const size = (one: unknown) => (isRecord(one) ? listOf(one.choices).length : 0);\n if (other !== undefined && size(bank) !== size(other)) {\n miss(\n ['banks', index, 'choices'],\n `Bank ${index + 1} offers ${count(size(other), 'choice')}; the exemplar's offers ${size(bank)}.`,\n );\n }\n });\n const myDraws = gapBanks(exemplar);\n const theirDraws = gapBanks(candidate);\n mine.forEach(({ value: gap }, index) => {\n const at = theirs[index]?.at;\n const other = theirs[index]?.value;\n if (at === undefined) {\n return;\n }\n if (myDraws[index] !== theirDraws[index]) {\n miss(\n ['gaps', at],\n `Gap ${index + 1} draws on its choices differently from the exemplar's.`,\n );\n } else if (myDraws[index] === null) {\n const size = (one: unknown) => (isRecord(one) ? listOf(one.choices).length : 0);\n if (size(gap) !== size(other)) {\n miss(\n ['gaps', at, 'choices'],\n `Gap ${index + 1} offers ${count(size(other), 'choice')}; the exemplar's offers ${size(gap)}.`,\n );\n }\n }\n });\n if ((exemplar.shuffleChoices === true) !== (candidate.shuffleChoices === true)) {\n miss(['shuffleChoices'], 'Shuffling the choices differs from the exemplar.');\n }\n }\n return misses;\n}\n\n/**\n * How `candidate`'s structure differs from `exemplar`'s: what makes the two\n * no parallel items — the type, the counts a learner answers (options and how\n * many are right, blanks, gaps, banks, a part's items and speakers), and the\n * settings that score them. Empty when they are parallel. Lengths, copies and\n * keys are not structure; see {@link lengthMisses}, {@link textSimilarity} and\n * {@link keysOf}.\n */\nexport function structureMisses(exemplar: unknown, candidate: unknown): StructureMiss[] {\n if (!isRecord(exemplar) || !isRecord(candidate)) {\n return [];\n }\n if (exemplar.type !== candidate.type) {\n return [\n {\n path: ['type'],\n message: `This is ${stringOf(candidate.type) || 'no type'}; the exemplar is ${stringOf(exemplar.type)}.`,\n },\n ];\n }\n if (exemplar.type !== 'item-group') {\n return itemMisses(exemplar, candidate);\n }\n const misses: StructureMiss[] = [];\n const mine = isRecord(exemplar.stimulus) ? exemplar.stimulus : {};\n const theirs = isRecord(candidate.stimulus) ? candidate.stimulus : {};\n if (mine.kind !== theirs.kind) {\n misses.push({\n path: ['stimulus', 'kind'],\n message: `The material is ${stringOf(theirs.kind) || 'of no kind'}; the exemplar's is ${stringOf(mine.kind)}.`,\n });\n }\n const myScript = stimulusScript(mine);\n const theirScript = stimulusScript(theirs);\n if (myScript !== null) {\n if (theirScript === null) {\n misses.push({\n path: ['stimulus', 'script'],\n message: \"The exemplar's recording has a script; this has none.\",\n });\n } else {\n if (theirScript.speakers !== myScript.speakers) {\n misses.push({\n path: ['stimulus', 'script'],\n message: `${count(theirScript.speakers, 'speaker')}; the exemplar has ${myScript.speakers}.`,\n });\n }\n const [low, high] = turnBand(myScript.turns);\n if (theirScript.turns < low || theirScript.turns > high) {\n misses.push({\n path: ['stimulus', 'script'],\n message: `${count(theirScript.turns, 'turn')}; the exemplar has ${myScript.turns}, so ${low} to ${high}.`,\n });\n }\n }\n }\n if ((exemplar.shuffle ?? 'none') !== (candidate.shuffle ?? 'none')) {\n misses.push({ path: ['shuffle'], message: 'Shuffling differs from the exemplar.' });\n }\n const myItems = listOf(exemplar.items);\n const theirItems = listOf(candidate.items);\n if (myItems.length !== theirItems.length) {\n misses.push({\n path: ['items'],\n message: `${count(theirItems.length, 'item')}; the exemplar has ${myItems.length}.`,\n });\n }\n myItems.forEach((item, index) => {\n const other = theirItems[index];\n if (other !== undefined) {\n for (const found of structureMisses(item, other)) {\n misses.push({ path: ['items', String(index), ...found.path], message: found.message });\n }\n }\n });\n return misses;\n}\n","import { containsWords, fold, passageWithoutPlaceholders } from '../answer-leak.js';\nimport type { ItemFinding } from '../types/authoring.js';\nimport {\n AI_VARIANT_NEAR_COPY_THRESHOLD,\n keysOf,\n shownScore,\n textSimilarity,\n textsOf,\n wordsOf,\n} from '../variant-rules.js';\nimport { finding } from './findings.js';\nimport { type DraftFields, isRecord } from './issues.js';\n\n/**\n * The paper critic: what only a whole paper shows, over every item of a set —\n * loose questions and the items of groups alike. Each finding sits at one of\n * the items involved and names the others in `related`.\n */\n\nconst listOf = (value: unknown): readonly unknown[] => (Array.isArray(value) ? value : []);\nconst stringOf = (value: unknown): string => (typeof value === 'string' ? value : '');\n\ninterface PaperItem {\n path: string[];\n item: DraftFields;\n /** The group it sits in: its path, so its own material is not searched for its answers. */\n group?: string[];\n}\n\ninterface Place {\n path: string[];\n /** The item it belongs to, or the group whose material it is. */\n owner: string;\n words: string[];\n}\n\nconst key = (path: readonly string[]): string => path.join('.');\n\nfunction itemsOf(entries: readonly unknown[]): PaperItem[] {\n return entries.flatMap((entry, index): PaperItem[] => {\n if (!isRecord(entry)) {\n return [];\n }\n if (entry.type === 'item-group') {\n const group = [String(index)];\n return listOf(entry.items).flatMap((item, at) =>\n isRecord(item) ? [{ path: [...group, 'items', String(at)], item, group }] : [],\n );\n }\n return typeof entry.type === 'string' ? [{ path: [String(index)], item: entry }] : [];\n });\n}\n\n/** The single-answer multiple choices a learner can see the key position of: unshuffled. */\nfunction singleAnswer(item: DraftFields): { key: number; options: readonly unknown[] } | null {\n if (item.type !== 'multiple-choice' || item.mode !== 'single') {\n return null;\n }\n const options = listOf(item.options);\n const keys = options.flatMap((option, index) =>\n isRecord(option) && option.isCorrect === true ? [index] : [],\n );\n return keys.length === 1 ? { key: keys[0] as number, options } : null;\n}\n\n// ── Key positions ──────────────────────────────────────────────────────\n\n/** Questions a position needs before \"more than half\" reads as a habit, not chance. */\nconst POSITION_BIAS_MINIMUM = 6;\n/** Keys a set needs before \"never here\" reads as a habit. */\nconst POSITION_NEVER_MINIMUM = 8;\n\nconst letter = (position: number): string =>\n position < 26 ? String.fromCharCode(65 + position) : String(position + 1);\n\nfunction keyPositionBias(items: readonly PaperItem[]): ItemFinding[] {\n const placed = items.flatMap((one) => {\n if (one.item.shuffle === true) {\n return [];\n }\n const single = singleAnswer(one.item);\n return single === null ? [] : [{ one, key: single.key, size: single.options.length }];\n });\n if (placed.length < POSITION_BIAS_MINIMUM || placed.every((at) => at.key === placed[0]?.key)) {\n // Every key in one place is `set_key_position_same`'s to say.\n return [];\n }\n const counts = new Map<number, PaperItem[]>();\n for (const at of placed) {\n counts.set(at.key, [...(counts.get(at.key) ?? []), at.one]);\n }\n for (const [position, holders] of counts) {\n if (holders.length * 2 > placed.length) {\n return [\n {\n ...finding(\n 'set_key_position_bias',\n [],\n `${holders.length} of ${placed.length} single-answer questions put the right option at ${letter(position)}. Spread the keys across positions, or shuffle the options.`,\n ),\n related: holders.map((one) => one.path),\n },\n ];\n }\n }\n if (placed.length >= POSITION_NEVER_MINIMUM) {\n const everywhere = Math.min(...placed.map((at) => at.size));\n for (let position = 0; position < everywhere; position += 1) {\n if (!counts.has(position)) {\n return [\n {\n ...finding(\n 'set_key_position_bias',\n [],\n `None of ${placed.length} single-answer questions puts the right option at ${letter(position)}, which every one of them has. A learner can rule it out.`,\n ),\n related: placed.map((at) => at.one.path),\n },\n ];\n }\n }\n }\n return [];\n}\n\n// ── The key is the longest ─────────────────────────────────────────────\n\nconst LONGEST_MINIMUM = 5;\nconst LONGEST_SHARE = 0.6;\n\nfunction keyLongestBias(items: readonly PaperItem[]): ItemFinding[] {\n const judged = items.flatMap((one) => {\n const single = singleAnswer(one.item);\n if (\n single === null ||\n single.options.length < 3 ||\n !single.options.every(\n (option) =>\n isRecord(option) &&\n option.media === undefined &&\n typeof option.text === 'string' &&\n option.text.trim() !== '',\n )\n ) {\n return [];\n }\n const lengths = single.options.map(\n (option) => [...stringOf((option as DraftFields).text).trim()].length,\n );\n const keyLength = lengths[single.key] as number;\n const longest = lengths.every((length, index) => index === single.key || length < keyLength);\n return [{ one, longest }];\n });\n const longest = judged.filter((at) => at.longest);\n if (judged.length < LONGEST_MINIMUM || longest.length < judged.length * LONGEST_SHARE) {\n return [];\n }\n return [\n {\n ...finding(\n 'set_key_longest_bias',\n [],\n `In ${longest.length} of ${judged.length} single-answer questions the right option is the longest. A learner who picks the longest option scores well without knowing the answers.`,\n ),\n related: longest.map((at) => at.one.path),\n },\n ];\n}\n\n// ── Stems that repeat ──────────────────────────────────────────────────\n\n/** Stems shorter than this are not compared: \"Choose the correct answer.\" repeats by design. */\nconst STEM_MINIMUM_WORDS = 6;\n\nfunction stemDuplicates(items: readonly PaperItem[]): ItemFinding[] {\n const stems = items.flatMap((one) =>\n textsOf(one.item)\n .filter((text) => text.kind !== 'material' && wordsOf(text.text).length >= STEM_MINIMUM_WORDS)\n .map((text) => ({ one, path: [...one.path, ...text.path], text: text.text })),\n );\n const findings: ItemFinding[] = [];\n stems.forEach((later, at) => {\n let best: { path: string[]; score: number } | undefined;\n for (const earlier of stems.slice(0, at)) {\n if (earlier.one === later.one) {\n continue;\n }\n const score = textSimilarity(earlier.text, later.text);\n if (score >= AI_VARIANT_NEAR_COPY_THRESHOLD && (best === undefined || score > best.score)) {\n best = { path: earlier.path, score };\n }\n }\n if (best !== undefined) {\n findings.push({\n ...finding(\n 'set_stem_duplicate',\n later.path,\n best.score === 1\n ? 'This reads the same as another item of the set. One piece of knowledge answers both.'\n : `This reads like another item of the set (similarity ${shownScore(best.score)}). One piece of knowledge may answer both.`,\n ),\n related: [best.path],\n });\n }\n });\n return findings;\n}\n\n// ── An answer printed in another item ──────────────────────────────────\n\n/**\n * The 100 most common English words (Oxford English Corpus), with the forms\n * of be, have and do: a key that is one of them is everywhere, so finding it\n * in another item says nothing.\n */\nconst COMMON_WORDS: ReadonlySet<string> = new Set(\n `the be to of and a in that have i it for not on with he as you do at this but his by from they we say her she or an will my one all would there their what so up out if about who get which go me when make can like time no just him know take people into year your good some could them see other than then now look only come its over think also back after use two how our work first well way even new want because any these give day most us is are was were been being am has had having does did done`.split(\n ' ',\n ),\n);\n\n/** Whether an answer is worth looking for in other items: more than two characters, and not a common word alone. */\nfunction distinctive(words: readonly string[]): boolean {\n return (\n words.join(' ').length > 2 && !(words.length === 1 && COMMON_WORDS.has(words[0] as string))\n );\n}\n\n/** What a learner reads or hears of an item: its own text, and its options' and choices'. */\nfunction placeOf(one: PaperItem): Place {\n const item = one.item;\n const texts = [\n ...textsOf(item).map((text) => text.text),\n ...listOf(item.options).map((option) => (isRecord(option) ? stringOf(option.text) : '')),\n ...listOf(item.gaps).flatMap((gap) =>\n isRecord(gap)\n ? listOf(gap.choices).map((choice) => (isRecord(choice) ? stringOf(choice.text) : ''))\n : [],\n ),\n ...listOf(item.banks).flatMap((bank) =>\n isRecord(bank)\n ? listOf(bank.choices).map((choice) => (isRecord(choice) ? stringOf(choice.text) : ''))\n : [],\n ),\n ];\n return {\n path: one.path,\n owner: key(one.path),\n words: fold(passageWithoutPlaceholders(texts.join('\\n'))),\n };\n}\n\n/** An item's own words, where its answer must not already be: its stem, not its options. */\nfunction ownWords(item: DraftFields): string[] {\n return fold(\n passageWithoutPlaceholders(\n textsOf(item)\n .filter((text) => text.kind !== 'material')\n .map((text) => text.text)\n .join('\\n'),\n ),\n );\n}\n\nfunction answersInOtherItems(\n entries: readonly unknown[],\n items: readonly PaperItem[],\n): ItemFinding[] {\n const places: Place[] = [\n ...items.map(placeOf),\n ...entries.flatMap((entry, index): Place[] => {\n if (!isRecord(entry) || entry.type !== 'item-group') {\n return [];\n }\n const material = textsOf({ type: 'item-group', stimulus: entry.stimulus })\n .map((text) => text.text)\n .join('\\n');\n return material === ''\n ? []\n : [{ path: [String(index), 'stimulus'], owner: `group:${index}`, words: fold(material) }];\n }),\n ];\n const findings: ItemFinding[] = [];\n for (const one of items) {\n const own = ownWords(one.item);\n const ownGroup = one.group !== undefined ? `group:${one.group[0]}` : undefined;\n const reported = new Set<string>();\n for (const slot of keysOf(one.item)) {\n for (const answer of slot.answers) {\n const words = fold(answer);\n if (!distinctive(words) || containsWords(own, words)) {\n continue;\n }\n for (const place of places) {\n if (\n place.owner === key(one.path) ||\n place.owner === ownGroup ||\n reported.has(place.owner)\n ) {\n continue;\n }\n if (containsWords(place.words, words)) {\n reported.add(place.owner);\n findings.push({\n ...finding(\n 'set_item_answers_item',\n [...one.path, ...slot.path],\n `The answer \"${answer}\" is printed elsewhere in the set: a learner can find it there.`,\n ),\n related: [place.path],\n });\n }\n }\n }\n }\n }\n return findings;\n}\n\n// ── One answer, many items ─────────────────────────────────────────────\n\nconst REPEATED_KEY_MINIMUM = 3;\n\nfunction repeatedKeys(items: readonly PaperItem[]): ItemFinding[] {\n const holders = new Map<string, string[][]>();\n for (const one of items) {\n const seen = new Set<string>();\n for (const slot of keysOf(one.item)) {\n for (const answer of slot.answers) {\n if (!seen.has(answer)) {\n seen.add(answer);\n holders.set(answer, [...(holders.get(answer) ?? []), [...one.path, ...slot.path]]);\n }\n }\n }\n }\n return [...holders].flatMap(([answer, paths]) =>\n paths.length >= REPEATED_KEY_MINIMUM\n ? [\n {\n ...finding(\n 'set_key_repeated',\n paths[0] as string[],\n `\"${answer}\" is the answer to ${paths.length} items. The set tests less than its length suggests.`,\n ),\n related: paths.slice(1),\n },\n ]\n : [],\n );\n}\n\n/**\n * What only a whole paper shows, over every item of `entries` — loose\n * questions and the items of groups: keys clustered in one position, keys\n * that are the longest option, stems that repeat, an answer printed in another\n * item, and one answer to many items. Paths are from the set's root.\n */\nexport function paperFindings(entries: readonly unknown[]): ItemFinding[] {\n const items = itemsOf(entries);\n return [\n ...keyPositionBias(items),\n ...keyLongestBias(items),\n ...stemDuplicates(items),\n ...answersInOtherItems(entries, items),\n ...repeatedKeys(items),\n ];\n}\n","import { UnknownActivityTypeError } from '../errors.js';\nimport { getActivityTypeDescriptor } from '../registry/index.js';\nimport { GROUP_CAPTIONS_REVEAL_DICTATION, groupCaptionsField } from '../schemas/dictation.js';\nimport { ItemGroupSchema, validateItemGroup } from '../schemas/item-group.js';\nimport { INTERACTIVE_VIDEO_ITEM_TYPES, isInteractiveVideoItemType } from '../timeline-limits.js';\nimport type { ActivityType } from '../types/activity.js';\nimport type {\n DraftContext,\n DraftIssue,\n DraftValidationResult,\n ItemFinding,\n} from '../types/authoring.js';\nimport type { ItemGroup } from '../types/item-group.js';\nimport { withValidationScope } from '../validation-scope.js';\nimport { keyPositionFinding } from './critique.js';\nimport { critiqueDraft, validateDraft } from './index.js';\nimport {\n checkMedia,\n covers,\n type DraftFields,\n isMissingId,\n isRecord,\n issue,\n isUnset,\n isUnwritten,\n refusesEmpty,\n} from './issues.js';\nimport { paperFindings } from './paper.js';\nimport { stimulusFindings } from './recordings.js';\n\n/**\n * A new, empty reading or listening testlet for an editor to start from.\n *\n * The stimulus starts as `text`, the only kind that needs no media file to be\n * a valid draft — an author who is writing a passage can begin typing, and one\n * who is building a listening item changes `kind` before uploading. `items`\n * starts empty, exactly as a new fill-in-the-blanks draft starts with no\n * blanks: the group is `incomplete` until somebody writes a question into it.\n *\n * @throws Error when `newId` returns an empty or repeated id.\n */\nexport function createItemGroupDraft(context: DraftContext): ItemGroup {\n const issued = new Set<string>();\n const newId = (): string => {\n const id: unknown = context.newId();\n if (typeof id !== 'string' || id === '') {\n throw new Error('createItemGroupDraft: newId() must return a non-empty string.');\n }\n if (issued.has(id)) {\n throw new Error(\n `createItemGroupDraft: newId() returned \"${id}\" twice. Ids within one draft must differ.`,\n );\n }\n issued.add(id);\n return id;\n };\n return {\n schemaVersion: '1.0',\n type: 'item-group',\n id: newId(),\n stimulus: { id: newId(), kind: 'text', body: '' },\n items: [],\n };\n}\n\n/**\n * A new, empty interactive video for an editor to start from: an item group\n * whose stimulus is a video with no file yet, no questions, and a timeline with\n * no quizzes.\n *\n * It is an ordinary item group — {@link validateItemGroupDraft} checks it, and\n * everything that reads groups reads it — because that is what keeps each\n * question its own slot. Like every new draft it is `incomplete`: an untouched\n * draft must never read as finished.\n *\n * @throws Error when `newId` returns an empty or repeated id.\n */\nexport function createInteractiveVideoDraft(context: DraftContext): ItemGroup {\n const draft = createItemGroupDraft(context);\n return {\n ...draft,\n stimulus: { id: draft.stimulus.id, kind: 'video', media: { type: 'video', url: '' } },\n timeline: { cues: [] },\n };\n}\n\n/**\n * Tells an unfinished testlet from a broken one — {@link validateDraft} for the\n * container that holds several questions around one passage or recording.\n *\n * It is a **separate entry point rather than a `'item-group'` type**, because\n * `validateDraft('item-group', …)` throws by design: `item-group` is reserved\n * in the registry precisely so it can never be registered as an activity, and\n * that guard is what keeps `isItemGroup` and `flattenSequence` able to trust\n * their own container. Widening `validateDraft` to accept it would have meant\n * weakening that.\n *\n * Each item is checked by `validateDraft` for its own type and its issues are\n * re-pathed under `items.N.…`, so an editor gets one list for the whole group\n * and every code in it is the code that type already documents. An item whose\n * type nobody registered is REPORTED, never thrown — an author fixing a\n * six-item group wants all six problems, not the first one that blew up.\n *\n * The group is `complete` only when the container passes, every item passes its\n * own schema, and every item is itself `complete`.\n *\n * @throws the error a registered item type's `checkDraft` or schema throws, as\n * `validateDraft` does for that item on its own. Only an unregistered type is\n * turned into an issue: a fault in a type's own code is not a problem with the\n * draft, and reporting it as an unknown type would hide it.\n */\n/**\n * The item critic over a testlet: each item's findings, under `items.N.…`, and\n * what only the set shows — every single-answer question keeping its right\n * option in one place. The stimulus is not searched for answers: a reading\n * group's answers are meant to be in its passage.\n *\n * An item of a type that is not registered, or without a `type`, is skipped:\n * {@link validateItemGroupDraft} already reports it.\n */\nexport function critiqueItemGroupDraft(draft: unknown): ItemFinding[] {\n if (!isRecord(draft)) {\n return [];\n }\n const items = Array.isArray(draft.items) ? draft.items : [];\n return withValidationScope(() => [\n ...stimulusFindings(draft.stimulus),\n ...items.flatMap((item, index) => repathed(critiqueItem(item), ['items', index])),\n ...keyPositionFinding(items, ['items']),\n ]);\n}\n\n/**\n * The item critic over a question set, as `<ActivitySequence>` takes one: each\n * entry's findings under its index — an item group's under `N.items.M.…` — and,\n * over the entries that are items, what only the set shows (see\n * {@link critiqueItemGroupDraft}). An entry of a type that is not registered is\n * skipped.\n */\nexport function critiqueDrafts(entries: readonly unknown[]): ItemFinding[] {\n return withValidationScope(() => [\n ...entries.flatMap((entry, index) =>\n repathed(\n isRecord(entry) && entry.type === 'item-group'\n ? critiqueItemGroupDraft(entry)\n : critiqueItem(entry),\n [index],\n ),\n ),\n ...keyPositionFinding(entries, []),\n ...paperFindings(entries),\n ]);\n}\n\n/** One item's findings, or none for an item the critic cannot place. */\nfunction critiqueItem(item: unknown): ItemFinding[] {\n if (!isRecord(item) || typeof item.type !== 'string') {\n return [];\n }\n if (getActivityTypeDescriptor(item.type) === undefined) {\n return [];\n }\n return critiqueDraft(item.type as ActivityType, item);\n}\n\nconst repathed = (findings: ItemFinding[], prefix: (string | number)[]): ItemFinding[] =>\n findings.map((found) => ({ ...found, path: [...prefix.map(String), ...found.path] }));\n\nexport function validateItemGroupDraft(draft: unknown): DraftValidationResult<ItemGroup> {\n // Every item is checked as a draft and then again as part of the group: one\n // scope lets the expensive normalisation behind both run once.\n return withValidationScope(() => validateItemGroupDraftInScope(draft));\n}\n\nfunction validateItemGroupDraftInScope(draft: unknown): DraftValidationResult<ItemGroup> {\n const issues: DraftIssue[] = [];\n if (!isRecord(draft)) {\n issues.push(\n issue('ig_not_an_object', [], 'A group must be an object with a stimulus and its items.'),\n );\n return { status: 'invalid', issues };\n }\n\n issues.push(...checkGroupIdentity(draft));\n if (isRecord(draft.stimulus)) {\n issues.push(...checkStimulus(draft.stimulus));\n } else if (isUnset(draft.stimulus)) {\n issues.push(\n issue(\n 'ig_stimulus_required',\n ['stimulus'],\n 'Add the passage, recording or image the questions are about.',\n ),\n );\n }\n issues.push(...checkItems(draft));\n if (draft.timeline !== undefined && draft.timeline !== null) {\n issues.push(...checkTimeline(draft));\n }\n const captions = groupCaptionsField(draft, (captionsUrl) => !isUnwritten(captionsUrl));\n if (captions !== undefined) {\n issues.push(\n issue(\n 'dc_captions_not_allowed',\n ['stimulus', 'media', captions],\n GROUP_CAPTIONS_REVEAL_DICTATION,\n ),\n );\n }\n\n // Every container failure the checks above did not already account for, added\n // as `invalid` — the same rule `validateDraft` follows, so a rule nobody\n // named still reaches the author instead of passing silently.\n const parsed = ItemGroupSchema.safeParse(draft, { reportInput: true });\n if (!parsed.success) {\n for (const schemaIssue of parsed.error.issues) {\n const path = schemaIssue.path.map(String);\n if (issues.some((known) => covers(known.path, path))) {\n continue;\n }\n if (path.length > 0 && refusesEmpty(draft, schemaIssue)) {\n issues.push(\n issue(\n 'null_not_allowed',\n path,\n 'This field cannot be null. Leave it out if it is optional, or give it a value.',\n ),\n );\n continue;\n }\n issues.push({\n path,\n message: schemaIssue.message,\n code: schemaIssue.code,\n severity: 'invalid',\n });\n }\n }\n\n const stored = validateItemGroup(draft);\n if (issues.length === 0 && stored.success) {\n return { status: 'complete', data: stored.data, issues };\n }\n if (issues.length === 0) {\n // The container and every item satisfy their own checks, yet the group is\n // not storable. Nothing here can name the cause, so hand over the\n // validator's own errors rather than claim the draft is finished.\n for (const error of stored.success ? [] : stored.errors) {\n issues.push({ ...error, severity: 'invalid' });\n }\n }\n const invalid = issues.some((found) => found.severity !== 'incomplete');\n return { status: invalid ? 'invalid' : 'incomplete', issues };\n}\n\n/**\n * An interactive video: every question in exactly one quiz, quizzes at real\n * moments, a video to hang them on, and only the five question types a video\n * may hold. Each check stands aside where another already names the problem —\n * an unset item type is `ig_item_type_required`, an unregistered one\n * `ig_item_type_unknown` — so an author sees one issue per mistake.\n */\nfunction checkTimeline(draft: DraftFields): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const timeline = draft.timeline;\n if (!isRecord(timeline)) {\n return issues;\n }\n const stimulus = isRecord(draft.stimulus) ? draft.stimulus : undefined;\n if (stimulus !== undefined && !isUnwritten(stimulus.kind) && stimulus.kind !== 'video') {\n issues.push(\n issue(\n 'ig_timeline_stimulus_kind',\n ['stimulus', 'kind'],\n 'An interactive video needs a video: quizzes open at moments of it.',\n ),\n );\n } else if (\n stimulus !== undefined &&\n isRecord(stimulus.media) &&\n !isUnwritten(stimulus.media.type) &&\n stimulus.media.type !== 'video'\n ) {\n issues.push(\n issue(\n 'ig_timeline_stimulus_kind',\n ['stimulus', 'media', 'type'],\n 'Upload a video file. An embedded player from another site cannot be paused when a quiz opens.',\n ),\n );\n }\n if (draft.shuffle === 'within-group') {\n issues.push(\n issue(\n 'ig_timeline_shuffle',\n ['shuffle'],\n 'The questions of an interactive video appear in the order their quizzes open, so they cannot be shuffled.',\n ),\n );\n }\n\n const items = Array.isArray(draft.items) ? draft.items : [];\n const itemIndexById = new Map<string, number>();\n items.forEach((item, index) => {\n if (!isRecord(item)) {\n return;\n }\n if (typeof item.id === 'string' && item.id !== '') {\n itemIndexById.set(item.id, index);\n }\n const type = item.type;\n // Registered but not one of the six: the unset and unregistered cases are\n // already `checkItems`'s to report.\n if (\n typeof type === 'string' &&\n type !== '' &&\n type !== 'item-group' &&\n !isInteractiveVideoItemType(type) &&\n getActivityTypeDescriptor(type) !== undefined\n ) {\n issues.push(\n issue(\n 'ig_timeline_item_type',\n ['items', index, 'type'],\n `An interactive video can hold ${INTERACTIVE_VIDEO_ITEM_TYPES.join(', ')} questions only.`,\n ),\n );\n }\n if (type === 'dictation' && isUnset(item.media)) {\n issues.push(\n issue(\n 'ig_timeline_dictation_media',\n ['items', index, 'media'],\n 'Add the recording this dictation plays: inside a video it cannot play the video.',\n ),\n );\n }\n });\n\n const cues = Array.isArray(timeline.cues) ? timeline.cues : [];\n const quizIds = new Set<string>();\n const placed = new Set<string>();\n cues.forEach((cue, cueIndex) => {\n if (!isRecord(cue)) {\n return;\n }\n const at = (field: string, ...rest: (string | number)[]): (string | number)[] => [\n 'timeline',\n 'cues',\n cueIndex,\n field,\n ...rest,\n ];\n if (isMissingId(cue.id)) {\n issues.push(issue('ig_timeline_quiz_id_required', at('id'), 'This quiz has no id.'));\n } else if (typeof cue.id === 'string') {\n if (quizIds.has(cue.id)) {\n issues.push(\n issue('ig_timeline_quiz_id_duplicate', at('id'), `Two quizzes share the id \"${cue.id}\".`),\n );\n }\n quizIds.add(cue.id);\n }\n if (isUnset(cue.at)) {\n issues.push(\n issue(\n 'ig_timeline_quiz_time_required',\n at('at'),\n 'Choose when in the video this quiz opens.',\n ),\n );\n } else if (typeof cue.at !== 'number' || !Number.isFinite(cue.at) || cue.at < 0) {\n issues.push(\n issue(\n 'ig_timeline_quiz_time_invalid',\n at('at'),\n 'A quiz opens at a number of seconds, 0 or more.',\n ),\n );\n }\n if (isUnset(cue.itemIds) || (Array.isArray(cue.itemIds) && cue.itemIds.length === 0)) {\n issues.push(\n issue('ig_timeline_quiz_empty', at('itemIds'), 'Add at least one question to this quiz.'),\n );\n return;\n }\n if (!Array.isArray(cue.itemIds)) {\n return;\n }\n cue.itemIds.forEach((itemId: unknown, position: number) => {\n if (typeof itemId !== 'string') {\n return;\n }\n if (!itemIndexById.has(itemId)) {\n issues.push(\n issue(\n 'ig_timeline_quiz_unknown_item',\n at('itemIds', position),\n `This quiz names a question that is not in the video (\"${itemId}\").`,\n ),\n );\n } else if (placed.has(itemId)) {\n issues.push(\n issue(\n 'ig_timeline_item_duplicate',\n at('itemIds', position),\n 'This question is already in a quiz: each question belongs to one quiz.',\n ),\n );\n }\n placed.add(itemId);\n });\n });\n for (const [itemId, index] of itemIndexById) {\n if (!placed.has(itemId)) {\n issues.push(\n issue(\n 'ig_timeline_item_unplaced',\n ['items', index],\n 'Add this question to a quiz, or the video never shows it.',\n ),\n );\n }\n }\n\n const chapters = Array.isArray(timeline.chapters) ? timeline.chapters : [];\n let previousAt: number | undefined;\n chapters.forEach((chapter, index) => {\n if (!isRecord(chapter)) {\n return;\n }\n if (isUnwritten(chapter.title)) {\n issues.push(\n issue(\n 'ig_timeline_chapter_title',\n ['timeline', 'chapters', index, 'title'],\n 'Give this chapter a title.',\n ),\n );\n }\n if (typeof chapter.at === 'number' && Number.isFinite(chapter.at)) {\n if (previousAt !== undefined && chapter.at <= previousAt) {\n issues.push(\n issue(\n 'ig_timeline_chapter_order',\n ['timeline', 'chapters', index, 'at'],\n 'Chapters must start in order, each after the one before it.',\n ),\n );\n }\n previousAt = chapter.at;\n }\n });\n return issues;\n}\n\n/** The group's own envelope. `title` is optional on a group, unlike an activity. */\nfunction checkGroupIdentity(draft: DraftFields): DraftIssue[] {\n const issues: DraftIssue[] = [];\n if (draft.schemaVersion !== '1.0') {\n issues.push(\n issue(\n 'schema_version_invalid',\n ['schemaVersion'],\n 'The draft must have schemaVersion \"1.0\". A draft from createItemGroupDraft has it.',\n ),\n );\n }\n if (draft.type !== 'item-group') {\n issues.push(issue('type_mismatch', ['type'], 'The draft\\'s type must be \"item-group\".'));\n }\n if (isMissingId(draft.id)) {\n issues.push(issue('id_required', ['id'], 'The group has no id.'));\n }\n if (!isUnset(draft.shuffle) && draft.shuffle !== 'none' && draft.shuffle !== 'within-group') {\n issues.push(\n issue(\n 'ig_shuffle_invalid',\n ['shuffle'],\n 'Shuffling is either \"none\" or \"within-group\". Leave it out to keep the authored order.',\n ),\n );\n }\n return issues;\n}\n\nconst KINDS = new Set(['text', 'audio', 'video', 'image', 'mixed']);\n/** Which media type each stimulus kind accepts — mirrors `StimulusSchema`'s refinement. */\nconst MEDIA_FOR_KIND: Record<string, readonly string[]> = {\n audio: ['audio'],\n video: ['video', 'embed'],\n image: ['image'],\n mixed: ['audio', 'video', 'image', 'embed'],\n};\n\n/**\n * The passage, recording or image. The split this makes is the one the whole\n * draft contract exists for: a kind nobody has chosen and a body nobody has\n * typed are `incomplete`, while a recording attached to an image stimulus is\n * `invalid` — writing more will not reconcile them.\n */\nfunction checkStimulus(stimulus: DraftFields): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const at = (...rest: (string | number)[]) => ['stimulus', ...rest];\n\n if (isMissingId(stimulus.id)) {\n issues.push(issue('ig_stimulus_id_required', at('id'), 'The stimulus has no id.'));\n }\n\n const kind = stimulus.kind;\n if (isUnwritten(kind)) {\n issues.push(\n issue(\n 'ig_stimulus_kind_required',\n at('kind'),\n 'Choose what the questions are about: a text, a recording, a video, an image, or a mix.',\n ),\n );\n } else if (typeof kind !== 'string' || !KINDS.has(kind)) {\n // A value of another type is the schema's to refuse.\n if (typeof kind === 'string') {\n issues.push(\n issue(\n 'ig_stimulus_kind_invalid',\n at('kind'),\n `\"${kind}\" is not a stimulus kind. Use text, audio, video, image or mixed.`,\n ),\n );\n }\n // Only the kind-DEPENDENT rules are skipped. The media still has problems\n // of its own — a refused address, a missing description — and returning\n // here left every one of them to the schema, so an unrecognised kind made\n // zod's raw diagnostics appear beside ours.\n if (isRecord(stimulus.media)) {\n issues.push(...checkMedia(stimulus.media, at('media')));\n }\n return issues;\n }\n\n const hasBody = typeof stimulus.body === 'string' && stimulus.body.trim() !== '';\n // One rule, one issue, at `body` — where the schema reports both halves of\n // it. Written as two branches, a `text` stimulus carrying rich text and no\n // plain text reported the same path twice; and keying the second branch on\n // whether `bodyHtml` had CONTENT missed the empty string a cleared rich-text\n // editor leaves behind, which the schema still refuses.\n const htmlPresent = !isUnset(stimulus.bodyHtml);\n if (!hasBody && (kind === 'text' || kind === 'mixed' || htmlPresent)) {\n issues.push(\n issue(\n 'ig_stimulus_body_required',\n at('body'),\n isUnwritten(stimulus.bodyHtml)\n ? 'Write the passage.'\n : 'Write the passage as plain text too. The rich-text version is shown only where a sanitiser is supplied.',\n ),\n );\n }\n\n if (kind !== 'text' && isUnset(stimulus.media)) {\n issues.push(\n issue('ig_stimulus_media_required', at('media'), 'Add the recording, video or image.'),\n );\n } else if (isRecord(stimulus.media)) {\n const allowed = MEDIA_FOR_KIND[kind as string];\n const mediaType = stimulus.media.type;\n if (allowed !== undefined && typeof mediaType === 'string' && !allowed.includes(mediaType)) {\n issues.push(\n issue(\n 'ig_stimulus_media_kind',\n at('media', 'type'),\n `A \"${kind}\" stimulus cannot carry ${mediaType} media. Use ${allowed.join(' or ')}.`,\n ),\n );\n }\n issues.push(...checkMedia(stimulus.media, at('media')));\n }\n return issues;\n}\n\n/**\n * Every item, checked by `validateDraft` for its own type.\n *\n * Re-pathing rather than re-implementing is the point: a multiple-choice\n * question inside a testlet reports exactly the codes a standalone one does,\n * so a consumer's translation table covers both and a rule can never drift\n * between them.\n */\nfunction checkItems(draft: DraftFields): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const items = isUnset(draft.items) ? [] : draft.items;\n if (!Array.isArray(items)) {\n return issues;\n }\n if (items.length === 0) {\n issues.push(\n issue('ig_items_required', ['items'], 'Add at least one question about this material.'),\n );\n return issues;\n }\n\n const seen = new Set<string>();\n const repeated = new Set<string>();\n for (const [index, item] of items.entries()) {\n if (!isRecord(item)) {\n continue;\n }\n const ordinal = index + 1;\n if (isMissingId(item.id)) {\n issues.push(\n issue('ig_item_id_required', ['items', index, 'id'], `Question ${ordinal} has no id.`),\n );\n } else if (typeof item.id === 'string') {\n if (seen.has(item.id)) {\n repeated.add(item.id);\n }\n seen.add(item.id);\n }\n\n if (item.type === 'item-group') {\n issues.push(\n issue(\n 'ig_item_nested_group',\n ['items', index, 'type'],\n 'Groups do not nest: every item must be an activity.',\n ),\n );\n continue;\n }\n if (isUnwritten(item.type)) {\n issues.push(\n issue(\n 'ig_item_type_required',\n ['items', index, 'type'],\n `Choose what kind of question ${ordinal} is.`,\n ),\n );\n continue;\n }\n if (typeof item.type !== 'string') {\n continue;\n }\n\n let result: DraftValidationResult<unknown>;\n try {\n result = validateDraft(item.type as ActivityType, item);\n } catch (error) {\n // Only an unregistered type is reported, never thrown — the same choice\n // `validateItemGroup` makes, for the same reason. Anything else is a fault\n // in a registered type's own checks or schema: it is rethrown, as\n // `validateDraft` throws it for the item on its own, because reported here\n // it read as an unknown type and sent the author after a mistake the draft\n // did not have. The error must name this item's type, too: a check that\n // validates a part of the item as some other, unregistered type has not\n // made this item's type unknown.\n if (!(error instanceof UnknownActivityTypeError) || error.activityType !== item.type) {\n throw error;\n }\n issues.push(\n issue(\n 'ig_item_type_unknown',\n ['items', index, 'type'],\n `\"${item.type}\" is not a registered activity type.`,\n ),\n );\n continue;\n }\n for (const found of result.issues) {\n issues.push({ ...found, path: ['items', String(index), ...found.path] });\n }\n }\n\n for (const id of repeated) {\n issues.push(\n issue('ig_item_id_duplicate', ['items'], `More than one question has the id \"${id}\".`),\n );\n }\n return issues;\n}\n","import { UnknownActivityTypeError } from '../errors.js';\nimport { getActivityTypeDescriptor } from '../registry/index.js';\nimport { readSchema } from '../schemas/read-schema.js';\nimport type { ActivityDataMap, ActivityType } from '../types/activity.js';\nimport type {\n DraftContext,\n DraftIssue,\n DraftValidationResult,\n ItemFinding,\n} from '../types/authoring.js';\nimport { withValidationScope } from '../validation-scope.js';\nimport { normaliseFinding } from './findings.js';\nimport {\n coveredPathsOf,\n DRAFT_ISSUE_SEVERITY,\n type DraftIssueCode,\n isRecord,\n issue,\n pathKey,\n refusesEmpty,\n} from './issues.js';\nimport { recordingFindings } from './recordings.js';\n\n/**\n * Tells a draft that is not finished from a draft that is wrong.\n *\n * `validateActivity` answers \"may this be stored?\", and to that question an\n * empty new question and a broken one are the same answer: no. An editor needs\n * the difference — \"Add a title\" is a to-do, \"Only one option can be marked\n * correct\" is an error — so a freshly added question does not read as a\n * failure, disable a whole form, or blank a preview.\n *\n * - `complete` — the draft is a valid activity with nothing left to write.\n * `data` is exactly what `validateActivity` returns for it.\n * - `incomplete` — every problem is something not yet written.\n * - `invalid` — at least one problem is wrong in a way writing more cannot fix.\n *\n * It is stricter than `validateActivity`, never looser: `complete` requires the\n * activity's schema to pass, and a draft can fail here that the schema accepts\n * (a title that is only whitespace). Content stored by an older build can\n * therefore be valid and still not `complete`. `validateActivity` stays the\n * check at your write boundary.\n *\n * Issues come from the type's `authoring.checkDraft`, plus every schema failure\n * it did not already report at that path or inside it. (A failure at the root of\n * the draft counts as reported only by an issue at the root, since every path is\n * inside the root.) Such a failure is added as `invalid`: under the code\n * `null_not_allowed` when the schema refused a `null` there, or an `undefined`\n * entry in a list, and otherwise under the schema's own code and message. A type\n * with no `checkDraft` therefore reports every schema failure as `invalid`.\n *\n * The codes documented in `docs/authoring.md` are a contract, and each is\n * reported with its documented severity, whichever check reports it. A code\n * passed through from the schema is not a contract: the code and its English\n * message belong to the schema library, so give a code you do not recognise a\n * generic message of your own.\n *\n * @throws UnknownActivityTypeError when `type` has no registered descriptor —\n * including `'item-group'`, which is a container, not an activity type.\n */\nexport function validateDraft<T extends ActivityType>(\n type: T,\n draft: unknown,\n): DraftValidationResult<ActivityDataMap[T]> {\n // The type's checks and its schema read the same strings: one scope lets the\n // expensive normalisation behind both run once.\n return withValidationScope(() => validateDraftInScope(type, draft));\n}\n\nfunction validateDraftInScope<T extends ActivityType>(\n type: T,\n draft: unknown,\n): DraftValidationResult<ActivityDataMap[T]> {\n const descriptor = getActivityTypeDescriptor(type);\n if (descriptor === undefined) {\n throw new UnknownActivityTypeError(String(type));\n }\n\n const reported =\n isRecord(draft) && descriptor.authoring?.checkDraft !== undefined\n ? descriptor.authoring.checkDraft(draft).map(normalise)\n : [];\n const issues: DraftIssue[] = [...reported];\n\n // `reportInput` records on each issue the value its check was given, which is\n // how a refused `null` is told apart from a rule that merely points at one.\n // A registered type's schema, read through Standard Schema, reports the value\n // at the issue's path instead.\n const parsed = readSchema(descriptor.schema, draft, `Activity type \"${descriptor.type}\"`, {\n reportInput: true,\n });\n if (!parsed.success) {\n const covered = coveredPathsOf(reported);\n const reportedEmpty = new Set<string>();\n for (const schemaIssue of parsed.issues) {\n const path = schemaIssue.path.map(String);\n if (covered.has(pathKey(path))) {\n continue;\n }\n // `null` is how many editors and databases spell \"no value\". Where the\n // schema refuses one, name it under one documented code, for every type,\n // rather than hand over the schema library's own diagnostic.\n if (path.length > 0 && refusesEmpty(draft, schemaIssue)) {\n const key = JSON.stringify(path);\n if (!reportedEmpty.has(key)) {\n reportedEmpty.add(key);\n issues.push(\n issue(\n 'null_not_allowed',\n path,\n typeof schemaIssue.path.at(-1) === 'number'\n ? 'This entry has no value. Remove it, or fill it in.'\n : 'This field cannot be null. Leave it out if it is optional, or give it a value.',\n ),\n );\n }\n continue;\n }\n issues.push({\n path,\n message: schemaIssue.message,\n code: schemaIssue.code,\n severity: 'invalid',\n });\n }\n }\n\n if (parsed.success && issues.length === 0) {\n return { status: 'complete', data: parsed.data as ActivityDataMap[T], issues };\n }\n const invalid = issues.some((found) => found.severity !== 'incomplete');\n return { status: invalid ? 'invalid' : 'incomplete', issues };\n}\n\n/**\n * A new, empty draft of an activity type, for an editor to start from.\n *\n * Every required field is present and nothing is authored, so the draft comes\n * back from {@link validateDraft} as `incomplete`, and `validateActivity`\n * rejects it until someone writes it. The SDK invents no ids: `newId` is called\n * once per id the draft needs, and must return a different non-empty string each\n * time.\n *\n * ```ts\n * const draft = createDraft('multiple-choice', { newId: () => crypto.randomUUID() });\n * ```\n *\n * @throws UnknownActivityTypeError when `type` has no registered descriptor.\n * @throws Error when the type declares no `authoring.createDraft`, or when\n * `newId` returns an empty or repeated id.\n */\nexport function createDraft<T extends ActivityType>(\n type: T,\n context: DraftContext,\n): ActivityDataMap[T] {\n const descriptor = getActivityTypeDescriptor(type);\n if (descriptor === undefined) {\n throw new UnknownActivityTypeError(String(type));\n }\n const create = descriptor.authoring?.createDraft;\n if (create === undefined) {\n throw new Error(\n `Activity type \"${String(type)}\" declares no authoring.createDraft, so there is no empty draft to create. ` +\n 'Build the draft yourself; validateDraft works for every registered type.',\n );\n }\n const issued = new Set<string>();\n const newId = (): string => {\n const id: unknown = context.newId();\n if (typeof id !== 'string' || id === '') {\n throw new Error('createDraft: newId() must return a non-empty string.');\n }\n if (issued.has(id)) {\n throw new Error(\n `createDraft: newId() returned \"${id}\" twice. Ids within one draft must differ — two options sharing an id cannot be scored apart.`,\n );\n }\n issued.add(id);\n return id;\n };\n return create({ newId }) as ActivityDataMap[T];\n}\n\n/**\n * A check's issue as `validateDraft` reports it.\n *\n * A documented code carries its documented severity, whichever check reports\n * it. Any other code keeps the check's severity, and one that is not\n * `'incomplete'` — misspelled, or missing — is reported as `invalid`, so the\n * mistake fails closed. Path segments become strings, as the schema's are, so a\n * list index given as a number still covers the schema's failure there.\n */\nfunction normalise(found: DraftIssue): DraftIssue {\n const documented = Object.hasOwn(DRAFT_ISSUE_SEVERITY, found.code)\n ? DRAFT_ISSUE_SEVERITY[found.code as DraftIssueCode]\n : undefined;\n return {\n ...found,\n path: found.path.map(String),\n severity: documented ?? (found.severity === 'incomplete' ? 'incomplete' : 'invalid'),\n };\n}\n\n/**\n * What an experienced item writer would point out on review: flaws a learner\n * can exploit or trip over (`warning`) and item-writing guidelines the item\n * departs from (`advice`) — the right option much longer than the rest, an\n * answer printed in the passage, a hint that gives the answer away, two\n * options that read the same, \"all of the above\" under shuffle.\n *\n * A finding is never a reason to refuse an item. It does not change what\n * {@link validateDraft} says: a valid item with findings is still valid. The\n * shape is a `DraftIssue`'s, so an editor shows both in one list.\n *\n * It reads a draft, finished or not, and skips any rule whose fields are not\n * written yet. The codes are documented in `docs/authoring.md`, each with its\n * severity; the rule behind a code is advice and may be refined in a minor.\n *\n * ```ts\n * const list = [...validateDraft(type, draft).issues, ...critiqueDraft(type, draft)];\n * ```\n *\n * @throws UnknownActivityTypeError when `type` has no registered descriptor —\n * including `'item-group'`: use {@link critiqueItemGroupDraft}.\n */\nexport function critiqueDraft(type: ActivityType, draft: unknown): ItemFinding[] {\n const descriptor = getActivityTypeDescriptor(type);\n if (descriptor === undefined) {\n throw new UnknownActivityTypeError(String(type));\n }\n if (!isRecord(draft)) {\n return [];\n }\n const critique = descriptor.authoring?.critique;\n return withValidationScope(() => [\n ...(critique === undefined ? [] : critique(draft)),\n // Every type's recordings, wherever its type keeps media.\n ...recordingFindings(draft),\n ]).map(normaliseFinding);\n}\n\nexport {\n createInteractiveVideoDraft,\n createItemGroupDraft,\n critiqueDrafts,\n critiqueItemGroupDraft,\n validateItemGroupDraft,\n} from './item-group.js';\n","import { cleanText, folded, needle, provenanceOf, usageOf } from './ai-text.js';\nimport { critiqueDraft } from './authoring/index.js';\nimport { UnknownActivityTypeError } from './errors.js';\nimport { getActivityTypeDescriptor } from './registry/index.js';\nimport type { ActivityType } from './types/activity.js';\nimport type {\n AiCritique,\n AiCritiqueField,\n AiCritiqueFinding,\n AiCritiqueKind,\n AiCritiqueRequest,\n AiRefusal,\n} from './types/ai.js';\n\n/** The most findings one critique may carry. More is refused, not cut. */\nexport const AI_CRITIQUE_MAX_FINDINGS = 20;\n\n/** The longest message or quote in a finding, in code points. */\nexport const AI_CRITIQUE_MAX_FIELD_LENGTH = 500;\n\nconst KINDS: readonly string[] = [\n 'ambiguous',\n 'second-answer',\n 'wrong-key',\n 'implausible-distractor',\n 'cue',\n 'level',\n 'language',\n 'sensitivity',\n 'other',\n] satisfies readonly AiCritiqueKind[];\n\n/**\n * Keys whose values are settings, ids or addresses — nothing a critique of the\n * item's words is about. A string under any of them is not a field to point at.\n */\nconst NOT_WORDS: ReadonlySet<string> = new Set([\n 'id',\n 'type',\n 'schemaVersion',\n 'mode',\n 'scoringStrategy',\n 'presentation',\n 'locale',\n 'languageTarget',\n 'correctChoiceId',\n 'bankId',\n 'url',\n 'captionsUrl',\n // A caption track's address and language, and a video's still: addresses\n // and settings, as `url` is.\n 'src',\n 'srclang',\n 'poster',\n // Who speaks a turn of a recording's script: a label for casting, not words.\n 'speaker',\n 'match',\n 'playback',\n 'recording',\n 'scoring',\n 'ai',\n 'learningObjectives',\n 'hints',\n 'kind',\n 'timeline',\n]);\n\n/**\n * Every string an author wrote into an item, with its path: the instructions,\n * the question, the options, the passage, the answers, the hints, the\n * feedback, a picture's `alt`. Settings, ids and addresses are left out, and\n * so is any `…Html` sidecar: the plain field beside it holds the same words.\n */\nfunction fieldsOf(value: unknown, path: string[] = []): AiCritiqueField[] {\n if (typeof value === 'string') {\n return value.trim() === '' ? [] : [{ path, text: value }];\n }\n if (Array.isArray(value)) {\n return value.flatMap((entry, index) => fieldsOf(entry, [...path, String(index)]));\n }\n if (typeof value === 'object' && value !== null) {\n return Object.entries(value).flatMap(([key, entry]) =>\n NOT_WORDS.has(key) || key.endsWith('Html') ? [] : fieldsOf(entry, [...path, key]),\n );\n }\n return [];\n}\n\n/**\n * What a model is given to review an item, or `null` when there is nothing to\n * review: a draft that is not an object, or one with no words in it yet.\n *\n * The facts are the item as the author wrote it — the answer key included; this\n * is for an author, never a learner — every text field a finding may point at,\n * and what the SDK's own critic already found there, so a model spends its\n * words on what a rule cannot see: a stem that reads two ways, a distractor\n * that is defensibly right, a key that is wrong, language above the level.\n *\n * Works for every registered type, the SDK's own and yours: the fields are read\n * from the draft itself.\n *\n * @throws UnknownActivityTypeError when `type` has no registered descriptor.\n */\nexport function aiCritiqueRequest(input: {\n type: ActivityType;\n draft: unknown;\n level?: string;\n authorLocale?: string;\n}): AiCritiqueRequest | null {\n const { type, draft, level, authorLocale } = input;\n if (getActivityTypeDescriptor(type) === undefined) {\n throw new UnknownActivityTypeError(String(type));\n }\n if (typeof draft !== 'object' || draft === null || Array.isArray(draft)) {\n return null;\n }\n const fields = fieldsOf(draft);\n if (fields.length === 0) {\n return null;\n }\n return {\n feature: 'item-critique',\n facts: {\n activityType: String(type),\n item: draft as Readonly<Record<string, unknown>>,\n fields,\n findings: critiqueDraft(type, draft).map(({ path, code, message }) => ({\n path,\n code,\n message,\n })),\n ...(level !== undefined ? { level } : {}),\n },\n ...(authorLocale !== undefined ? { authorLocale } : {}),\n };\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst tooLong = (text: string): boolean => [...text].length > AI_CRITIQUE_MAX_FIELD_LENGTH;\n\n/**\n * Checks a critique a port returned for `request`, and turns it into findings\n * an editor lists beside the SDK's own. As every AI answer, it is shown whole\n * or not at all.\n *\n * - **Every finding points at a field the item has.** Its `path` is one of\n * `facts.fields`, exactly; a path to a field the item does not have refuses\n * the whole reply as `contradicts-item`.\n * - **A quote is words that field contains**, typographic quotes and runs of\n * spacing aside, case kept; one it does not refuses the reply the same way.\n * - **Every finding is `advice`.** It is a model's opinion about the item, and\n * nothing in the SDK acts on it; its code is `ai_` and the kind\n * (`ai_second_answer`).\n * - **Limits:** {@link AI_CRITIQUE_MAX_FINDINGS} findings; a message or quote of\n * {@link AI_CRITIQUE_MAX_FIELD_LENGTH} code points; over them `too-long`. A\n * kind the SDK does not name, or any other shape, is `malformed`.\n */\nexport function checkAiCritique(\n raw: unknown,\n request: AiCritiqueRequest,\n): { ok: true; critique: AiCritique } | { ok: false; refusal: AiRefusal } {\n if (!isRecord(raw) || !Array.isArray(raw.findings)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (raw.findings.length > AI_CRITIQUE_MAX_FINDINGS) {\n return { ok: false, refusal: 'too-long' };\n }\n const fields = new Map(request.facts.fields.map((field) => [JSON.stringify(field.path), field]));\n const findings: AiCritiqueFinding[] = [];\n for (const entry of raw.findings) {\n if (\n !isRecord(entry) ||\n !Array.isArray(entry.path) ||\n !entry.path.every((segment) => typeof segment === 'string' || typeof segment === 'number') ||\n typeof entry.kind !== 'string' ||\n !KINDS.includes(entry.kind) ||\n typeof entry.message !== 'string' ||\n (entry.quote !== undefined && entry.quote !== null && typeof entry.quote !== 'string')\n ) {\n return { ok: false, refusal: 'malformed' };\n }\n const message = cleanText(entry.message);\n const quote = typeof entry.quote === 'string' ? cleanText(entry.quote) : undefined;\n if (message === '' || quote === '') {\n return { ok: false, refusal: 'malformed' };\n }\n if (tooLong(message) || (quote !== undefined && tooLong(quote))) {\n return { ok: false, refusal: 'too-long' };\n }\n const field = fields.get(JSON.stringify(entry.path.map(String)));\n if (field === undefined) {\n return { ok: false, refusal: 'contradicts-item' };\n }\n if (quote !== undefined && !folded(field.text).chars.includes(needle(quote))) {\n return { ok: false, refusal: 'contradicts-item' };\n }\n findings.push({\n path: field.path,\n code: `ai_${entry.kind.replace(/-/g, '_')}`,\n message,\n severity: 'advice',\n ...(quote !== undefined ? { quote } : {}),\n });\n }\n const provenance = provenanceOf(raw.provenance);\n const usage = usageOf(raw.usage);\n return {\n ok: true,\n critique: {\n findings,\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n","import { provenanceOf, usageOf } from './ai-text.js';\nimport { containsWords, fold } from './answer-leak.js';\nimport { keyPositionFinding } from './authoring/critique.js';\nimport {\n critiqueDraft,\n critiqueItemGroupDraft,\n validateDraft,\n validateItemGroupDraft,\n} from './authoring/index.js';\nimport type { ActivityMedia } from './types/activity.js';\nimport type {\n AiDraftCaption,\n AiDraftProblem,\n AiDraftSettings,\n AiDraftSource,\n AiDrafts,\n AiDraftsCall,\n AiDraftsRequest,\n AiDraftsRun,\n AiDraftType,\n AiGeneratedDraft,\n AiRefusal,\n AiVideoDraft,\n} from './types/ai.js';\n\n/** The most drafts one request may ask for, and the most a reply may hold when it names no count. */\nexport const AI_DRAFTS_MAX_COUNT = 50;\n\n/** The most repair passes `generateDrafts` makes. */\nexport const AI_DRAFTS_MAX_REPAIRS = 3;\n\nconst DRAFT_TYPES: readonly string[] = [\n 'multiple-choice',\n 'fill-in-the-blanks',\n 'gap-select',\n 'dictation',\n 'read-aloud',\n 'written-response',\n] satisfies readonly AiDraftType[];\n\ntype Fields = Record<string, unknown>;\n\nconst isRecord = (value: unknown): value is Fields =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n// ── The shape a model writes ───────────────────────────────────────────\n\nconst text = (description: string) => ({ type: 'string', description });\n\nconst FEEDBACK = {\n type: 'object',\n description: 'Optional overall feedback, shown after answering.',\n properties: {\n correct: text('Shown when the learner passes.'),\n incorrect: text('Shown when the learner does not.'),\n },\n};\n\n/** Every field a model writes, and the shape of each. A name means one thing in every type that uses it. */\nconst FIELDS: Readonly<Record<string, Fields>> = {\n title: text(\n 'A short name for the item, shown to the learner above it. Never the answer, or any part of it.',\n ),\n question: text('The question, as plain text.'),\n mode: {\n type: 'string',\n enum: ['single', 'multi'],\n description: '`single`: exactly one option is right. `multi`: one or more are.',\n },\n options: {\n type: 'array',\n minItems: 2,\n maxItems: 26,\n items: {\n type: 'object',\n required: ['text', 'isCorrect'],\n properties: {\n text: text('The option as the learner reads it.'),\n isCorrect: { type: 'boolean' },\n feedback: text('Why this option is right or wrong, shown after answering.'),\n },\n },\n },\n passage: text(\n 'The passage as plain text, with {{1}}, {{2}}, … where each gap goes, numbered in the order they appear.',\n ),\n blanks: {\n type: 'array',\n minItems: 1,\n description: 'One per gap, in the order the gaps appear: the first is {{1}}.',\n items: {\n type: 'object',\n required: ['acceptedAnswers'],\n properties: {\n acceptedAnswers: {\n type: 'array',\n minItems: 1,\n items: { type: 'string' },\n description: 'Every answer that is right, spelled as a learner would type it.',\n },\n hint: text('Optional. Helps without giving the answer or any of its words.'),\n },\n },\n },\n gaps: {\n type: 'array',\n minItems: 1,\n description: 'One per gap, in the order the gaps appear: the first is {{1}}.',\n items: {\n type: 'object',\n required: ['choices'],\n properties: {\n choices: {\n type: 'array',\n minItems: 2,\n description: 'What the learner chooses from. Exactly one is right.',\n items: {\n type: 'object',\n required: ['text', 'isCorrect'],\n properties: { text: text('A choice.'), isCorrect: { type: 'boolean' } },\n },\n },\n },\n },\n },\n transcript: text(\n 'What the learner hears and types: words from the source, as they are said there.',\n ),\n referenceText: text('What the learner reads aloud: a sentence or two from the source.'),\n instructions: text(\n 'Optional. The task, shown to the learner before the item: what to do (\"Complete the sentence. Use ONE word.\"), or how to read it aloud. Never part of the text the learner answers.',\n ),\n prompt: text('The writing task, as plain text.'),\n minWords: { type: 'integer', minimum: 0 },\n maxWords: { type: 'integer', minimum: 1 },\n rubric: {\n type: 'object',\n description: 'Optional. What the writing is assessed on; every criterion is weighted equally.',\n properties: {\n criteria: {\n type: 'array',\n minItems: 1,\n items: {\n type: 'object',\n required: ['name'],\n properties: { name: text('A short name.'), description: text('What it looks for.') },\n },\n },\n },\n },\n feedback: FEEDBACK,\n};\n\n/** The fields a model writes for each type: a draft's own names, never an id or a setting. */\nconst WRITES: Readonly<Record<AiDraftType, readonly string[]>> = {\n 'multiple-choice': ['title', 'instructions', 'question', 'mode', 'options', 'feedback'],\n 'fill-in-the-blanks': ['title', 'instructions', 'passage', 'blanks', 'feedback'],\n 'gap-select': ['title', 'instructions', 'passage', 'gaps', 'feedback'],\n dictation: ['title', 'transcript', 'feedback'],\n 'read-aloud': ['title', 'referenceText', 'instructions', 'feedback'],\n 'written-response': ['title', 'prompt', 'minWords', 'maxWords', 'rubric'],\n};\n\n/**\n * One draft, as a model writes it: its `type`, and the fields that type uses —\n * one object for every chosen type, since a structured output that must pick\n * between schemas is the thing providers support least.\n */\nfunction itemShape(types: readonly AiDraftType[], captions: boolean): Fields {\n const properties: Fields = {\n type: {\n type: 'string',\n enum: [...types],\n description: `The item's type, and the fields it fills. ${types\n .map((type) => `${type}: ${WRITES[type].filter((name) => name !== 'title').join(', ')}`)\n .join('. ')}.`,\n },\n };\n for (const type of types) {\n for (const name of WRITES[type]) {\n properties[name] = FIELDS[name];\n }\n }\n if (captions) {\n properties.caption = {\n type: 'integer',\n minimum: 0,\n description: 'The index of the caption the item is about. It opens when that caption ends.',\n };\n }\n return {\n type: 'object',\n required: captions ? ['type', 'title', 'caption'] : ['type', 'title'],\n properties,\n };\n}\n\n/** A reply of up to `max` drafts — exactly `exactly` on a repair. */\nfunction replyShape(\n types: readonly AiDraftType[],\n captions: boolean,\n max: number,\n exactly?: number,\n): Fields {\n return {\n type: 'object',\n required: ['drafts'],\n properties: {\n drafts: {\n type: 'array',\n ...(exactly !== undefined ? { minItems: exactly, maxItems: exactly } : { maxItems: max }),\n items: itemShape(types, captions),\n },\n },\n };\n}\n\n// ── The request ────────────────────────────────────────────────────────\n\n/**\n * What a model is given to draft items from a source, or `null` when there is\n * no source to draft from: an empty text, or no caption with words in it.\n *\n * `types` are the ones the author chose, in the author's order: the model\n * leans on the first, and where several drafts share a moment of a video — or\n * the end of one — they are shown in this order. `count` is how many to write\n * at most; left out, the model writes as many as the source is worth, up to\n * {@link AI_DRAFTS_MAX_COUNT}.\n *\n * The facts are the source — a caption list numbered, with its times in\n * seconds — the types, the count, and the language, level and instructions\n * the author gave. `shape` is the JSON Schema of the reply: each draft names\n * its type and writes that type's own fields, with no ids, no HTML, no media\n * and no settings. Those are not a model's to write. `settings` are yours, put\n * on every draft of a type: a read-aloud's `recording` and `scoring`, say, which\n * the SDK has no default for.\n *\n * @throws RangeError when `types` is empty, repeats a type or names one a model\n * does not draft, or `count` is not a whole number from 1 to\n * {@link AI_DRAFTS_MAX_COUNT}.\n */\nexport function aiDraftsRequest(input: {\n types: readonly AiDraftType[];\n source: AiDraftSource;\n count?: number;\n locale?: string;\n level?: string;\n instructions?: string;\n settings?: AiDraftSettings;\n}): AiDraftsRequest | null {\n const { types, source, count, locale, level, instructions, settings } = input;\n if (\n !Array.isArray(types) ||\n types.length === 0 ||\n new Set(types).size !== types.length ||\n !types.every((type) => DRAFT_TYPES.includes(type))\n ) {\n throw new RangeError(\n `aiDraftsRequest: types must be one or more of ${DRAFT_TYPES.join(', ')}, each once, in the author's order.`,\n );\n }\n if (\n count !== undefined &&\n (!Number.isInteger(count) || count < 1 || count > AI_DRAFTS_MAX_COUNT)\n ) {\n throw new RangeError(\n `aiDraftsRequest: count must be a whole number from 1 to ${AI_DRAFTS_MAX_COUNT}, or left out, not ${String(count)}.`,\n );\n }\n let facts: AiDraftsRequest['facts']['source'];\n if (source.kind === 'captions') {\n const captions: AiDraftCaption[] = (Array.isArray(source.cues) ? source.cues : []).flatMap(\n (cue, index) =>\n isRecord(cue) &&\n typeof cue.text === 'string' &&\n cue.text.trim() !== '' &&\n Number.isFinite(cue.start) &&\n Number.isFinite(cue.end)\n ? [{ index, start: cue.start, end: cue.end, text: cue.text }]\n : [],\n );\n if (captions.length === 0) {\n return null;\n }\n facts = { kind: 'captions', captions };\n } else {\n if (typeof source.text !== 'string' || source.text.trim() === '') {\n return null;\n }\n facts = { kind: source.kind, text: source.text };\n }\n return {\n feature: 'draft-generation',\n facts: {\n activityTypes: [...types],\n source: facts,\n ...(count !== undefined ? { count } : {}),\n ...(locale !== undefined ? { locale } : {}),\n ...(level !== undefined ? { level } : {}),\n ...(instructions !== undefined ? { instructions } : {}),\n },\n shape: replyShape(types, facts.kind === 'captions', count ?? AI_DRAFTS_MAX_COUNT),\n ...(settings !== undefined ? { settings } : {}),\n };\n}\n\n// ── From a reply to drafts ─────────────────────────────────────────────\n\nconst stringOf = (value: unknown): string => (typeof value === 'string' ? value : '');\nconst listOf = (value: unknown): readonly unknown[] => (Array.isArray(value) ? value : []);\nconst written = (value: unknown): string | undefined =>\n typeof value === 'string' && value.trim() !== '' ? value : undefined;\n\nfunction feedbackOf(entry: Fields): Fields {\n if (!isRecord(entry.feedback)) {\n return {};\n }\n const correct = written(entry.feedback.correct);\n const incorrect = written(entry.feedback.incorrect);\n return correct === undefined && incorrect === undefined\n ? {}\n : {\n feedback: {\n ...(correct !== undefined ? { correct } : {}),\n ...(incorrect !== undefined ? { incorrect } : {}),\n },\n };\n}\n\n/** The instructions a model wrote for the learner, when it wrote any. */\nfunction instructionsOf(entry: Fields): Fields {\n const instructions = written(entry.instructions);\n return instructions !== undefined ? { instructions } : {};\n}\n\n/** A type's content, read from what a model wrote. */\nfunction contentOf(type: AiDraftType, entry: Fields, newId: () => string): Fields {\n switch (type) {\n case 'multiple-choice':\n return {\n ...instructionsOf(entry),\n question: stringOf(entry.question),\n ...(typeof entry.mode === 'string' ? { mode: entry.mode } : {}),\n options: listOf(entry.options).map((option) => {\n const fields = isRecord(option) ? option : {};\n const feedback = written(fields.feedback);\n return {\n id: newId(),\n text: stringOf(fields.text),\n isCorrect: fields.isCorrect === true,\n ...(feedback !== undefined ? { feedback } : {}),\n };\n }),\n ...feedbackOf(entry),\n };\n case 'fill-in-the-blanks':\n return {\n ...instructionsOf(entry),\n passage: stringOf(entry.passage),\n blanks: listOf(entry.blanks).map((blank, index) => {\n const fields = isRecord(blank) ? blank : {};\n const hint = written(fields.hint);\n return {\n id: String(index + 1),\n acceptedAnswers: listOf(fields.acceptedAnswers).filter(\n (answer): answer is string => typeof answer === 'string',\n ),\n ...(hint !== undefined ? { hint } : {}),\n };\n }),\n ...feedbackOf(entry),\n };\n case 'gap-select':\n return {\n ...instructionsOf(entry),\n passage: stringOf(entry.passage),\n gaps: listOf(entry.gaps).map((gap, index) => {\n const marked = listOf(isRecord(gap) ? gap.choices : undefined);\n const choices = marked.map((choice) => ({\n id: newId(),\n text: stringOf(isRecord(choice) ? choice.text : undefined),\n }));\n // Exactly one marked right is a key; none, or several, is no key —\n // which validateDraft asks for, and a repair then supplies.\n const right = marked.flatMap((choice, at) =>\n isRecord(choice) && choice.isCorrect === true ? [at] : [],\n );\n return {\n id: String(index + 1),\n choices,\n correctChoiceId: right.length === 1 ? choices[right[0] as number]?.id : '',\n };\n }),\n ...feedbackOf(entry),\n };\n case 'dictation':\n return { transcript: stringOf(entry.transcript), ...feedbackOf(entry) };\n case 'read-aloud':\n return {\n referenceText: stringOf(entry.referenceText),\n ...instructionsOf(entry),\n ...feedbackOf(entry),\n };\n default: {\n const criteria = listOf(isRecord(entry.rubric) ? entry.rubric.criteria : undefined);\n return {\n prompt: stringOf(entry.prompt),\n ...(entry.minWords !== undefined ? { minWords: entry.minWords } : {}),\n ...(entry.maxWords !== undefined ? { maxWords: entry.maxWords } : {}),\n ...(criteria.length > 0\n ? {\n rubric: {\n criteria: criteria.map((criterion) => {\n const fields = isRecord(criterion) ? criterion : {};\n const description = written(fields.description);\n return {\n name: stringOf(fields.name),\n ...(description !== undefined ? { description } : {}),\n weight: 1,\n };\n }),\n },\n }\n : {}),\n };\n }\n }\n}\n\n/**\n * What a new draft of a type starts with, where the model writes nothing: a\n * scoring strategy on the three that have one — `all-or-nothing`, as a new\n * draft has — and a read-aloud's recording and scoring left unset, as a new\n * draft leaves them, for the host's settings or the author to fill.\n */\nfunction startOf(type: AiDraftType): Fields {\n switch (type) {\n case 'multiple-choice':\n case 'fill-in-the-blanks':\n case 'gap-select':\n return { scoringStrategy: 'all-or-nothing' };\n case 'read-aloud':\n return { recording: { maxSeconds: 0 }, scoring: { dimensions: [] } };\n default:\n return {};\n }\n}\n\n/**\n * A draft from what a model wrote. Lenient: every field it can read is kept as\n * written, and anything it cannot is left for `validateDraft` to name — which\n * is what a repair tells the model. Ids are the host's; a blank's and a gap's\n * are the numbers of their placeholders, as the passage names them. In order:\n * what a new draft starts with, the language asked for, the host's settings,\n * then what the model wrote — so a setting never overrides the content, and\n * nothing overrides what the draft is.\n */\nfunction draftFrom(\n type: AiDraftType,\n entry: Fields,\n id: string,\n newId: () => string,\n request: AiDraftsRequest,\n): Fields {\n const locale = request.facts.locale;\n return {\n ...startOf(type),\n ...(locale !== undefined ? { locale } : {}),\n ...request.settings?.[type],\n title: stringOf(entry.title),\n ...contentOf(type, entry, newId),\n schemaVersion: '1.0',\n type,\n id,\n };\n}\n\n/** Ids from the host, checked as `createDraft` checks them. */\nfunction idSource(newId: () => string, caller: string): () => string {\n const issued = new Set<string>();\n return () => {\n const id: unknown = newId();\n if (typeof id !== 'string' || id === '') {\n throw new Error(`${caller}: newId() must return a non-empty string.`);\n }\n if (issued.has(id)) {\n throw new Error(\n `${caller}: newId() returned \"${id}\" twice. Ids must differ — two options sharing an id cannot be scored apart.`,\n );\n }\n issued.add(id);\n return id;\n };\n}\n\n/** Drafts in the author's order of types, and within a type in the order the model wrote them. */\nfunction inAuthorsOrder(\n drafts: readonly AiGeneratedDraft[],\n types: readonly AiDraftType[],\n): AiGeneratedDraft[] {\n return [...drafts].sort(\n (a, b) => types.indexOf(a.type) - types.indexOf(b.type) || a.index - b.index,\n );\n}\n\nfunction check(\n raw: unknown,\n request: AiDraftsRequest,\n newId: () => string,\n keep: readonly (string | undefined)[] = [],\n): { ok: true; drafts: AiDrafts } | { ok: false; refusal: AiRefusal } {\n const types = request.facts.activityTypes;\n if (\n !isRecord(raw) ||\n !Array.isArray(raw.drafts) ||\n !raw.drafts.every((entry) => isRecord(entry) && types.includes(entry.type as AiDraftType))\n ) {\n return { ok: false, refusal: 'malformed' };\n }\n const entries = raw.drafts as Fields[];\n const repair = request.repair;\n const most = request.facts.count ?? AI_DRAFTS_MAX_COUNT;\n if (repair !== undefined ? entries.length !== repair.length : entries.length > most) {\n return { ok: false, refusal: repair !== undefined ? 'malformed' : 'too-long' };\n }\n const source = request.facts.source;\n const drafts = entries.map((entry, position): AiGeneratedDraft => {\n const type = entry.type as AiDraftType;\n const draft = draftFrom(type, entry, keep[position] ?? newId(), newId, request);\n const caption =\n source.kind === 'captions' && Number.isInteger(entry.caption)\n ? source.captions.find((one) => one.index === entry.caption)\n : undefined;\n // A dictation's recording can be cut from the video only where the video\n // says its words: the caption it named, holding its transcript.\n const said = fold(stringOf(entry.transcript));\n const clip =\n type === 'dictation' && caption !== undefined && containsWords(fold(caption.text), said)\n ? { start: caption.start, end: caption.end }\n : undefined;\n return {\n index: repair !== undefined ? (repair[position]?.index as number) : position,\n type,\n draft,\n validation: validateDraft(type, draft),\n findings: critiqueDraft(type, draft),\n ...(caption !== undefined ? { at: caption.end } : {}),\n ...(clip !== undefined ? { clip } : {}),\n generated: entry,\n };\n });\n const ordered = inAuthorsOrder(drafts, types);\n const provenance = provenanceOf(raw.provenance);\n const usage = usageOf(raw.usage);\n return {\n ok: true,\n drafts: {\n drafts: ordered,\n findings: keyPositionFinding(\n ordered.map((one) => one.draft),\n [],\n ),\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n\n/**\n * Reads a model's reply to {@link aiDraftsRequest} into drafts an editor can\n * show for approval: each with ids from `newId`, your settings for its type,\n * what `validateDraft` says of it, what the item critic finds in it, and — from\n * captions — where it goes, and for a dictation the stretch of the video that\n * says its words. They come back in the author's order of types.\n *\n * Drafts are independent: one that is unfinished or wrong is returned as such,\n * for an author or {@link aiDraftsRepairRequest} to fix, beside the ones that\n * are complete. `complete` means valid, never approved: a model wrote it, and a\n * person decides whether learners see it. The whole reply is refused only when\n * it is not `{ drafts: [...] }` of objects of the chosen types (`malformed`), or\n * holds more drafts than asked for (`too-long`) — on a repair, not exactly one\n * per draft to fix (`malformed`).\n *\n * Only the fields a type's shape names are read: a model cannot set an id,\n * HTML, media, a switch or a scoring setting through this. A question is scored\n * `all-or-nothing` unless your settings say otherwise, a written response's\n * criteria are weighted equally, and a dictation carries no recording — attach\n * one, or cut it from the video at `clip`.\n *\n * @throws Error when `newId` returns an empty or repeated id.\n */\nexport function checkAiDrafts(\n raw: unknown,\n request: AiDraftsRequest,\n options: { newId: () => string },\n): { ok: true; drafts: AiDrafts } | { ok: false; refusal: AiRefusal } {\n return check(raw, request, idSource(options.newId, 'checkAiDrafts'));\n}\n\n// ── Repair ─────────────────────────────────────────────────────────────\n\n/**\n * Where a model wrote what a problem names: the draft's paths, but a gap's key\n * is its choices, and a reading too long for the host's recording time is the\n * text the model chose.\n */\nfunction pathForModel(type: AiDraftType, problem: AiDraftProblem): string[] {\n const { path, code } = problem;\n if (\n type === 'gap-select' &&\n path.length === 3 &&\n path[0] === 'gaps' &&\n path[2] === 'correctChoiceId'\n ) {\n return ['gaps', path[1] as string, 'choices'];\n }\n return code === 'ra_text_long_for_time' ? ['referenceText'] : [...path];\n}\n\n/**\n * What is wrong with a draft that the model can fix: every issue and warning\n * at a field the model writes. A read-aloud with no recording time, or a\n * dictation in a video with no recording, is the host's to finish, and is not\n * sent back.\n */\nfunction problemsOf(one: AiGeneratedDraft): AiDraftProblem[] {\n const writes = WRITES[one.type];\n return [\n ...one.validation.issues.map(\n (found): AiDraftProblem => ({\n path: found.path,\n code: found.code,\n message: found.message,\n severity: found.severity,\n }),\n ),\n ...one.findings\n .filter((found) => found.severity === 'warning')\n .map(\n (found): AiDraftProblem => ({\n path: found.path,\n code: found.code,\n message: found.message,\n severity: 'warning',\n }),\n ),\n ]\n .map((problem) => ({ ...problem, path: pathForModel(one.type, problem) }))\n .filter((problem) => writes.includes(problem.path[0] as string));\n}\n\n/**\n * A second request, for the drafts with something wrong the model can fix —\n * unfinished, wrong, or a flaw the critic warns about, at a field the model\n * writes — each as the model wrote it, with those problems at the paths the\n * model wrote. `null` when there is none.\n *\n * The reply holds one draft for each, in order; {@link checkAiDrafts} reads it\n * and gives each the `index` of the draft it replaces.\n */\nexport function aiDraftsRepairRequest(\n request: AiDraftsRequest,\n drafts: AiDrafts | readonly AiGeneratedDraft[],\n): AiDraftsRequest | null {\n const list: readonly AiGeneratedDraft[] = 'drafts' in drafts ? drafts.drafts : drafts;\n const repair = list.flatMap((one) => {\n const problems = problemsOf(one);\n return problems.length === 0 ? [] : [{ index: one.index, draft: one.generated, problems }];\n });\n if (repair.length === 0) {\n return null;\n }\n const { repair: _previous, ...first } = request;\n return {\n ...first,\n shape: replyShape(\n request.facts.activityTypes,\n request.facts.source.kind === 'captions',\n repair.length,\n repair.length,\n ),\n repair,\n };\n}\n\n// ── The loop ───────────────────────────────────────────────────────────\n\nconst RANK = { complete: 2, incomplete: 1, invalid: 0 } as const;\n\n/** Whether a repaired draft is at least as good as the one it would replace. */\nfunction noWorse(repaired: AiGeneratedDraft, before: AiGeneratedDraft): boolean {\n const warnings = (one: AiGeneratedDraft): number =>\n one.findings.filter((found) => found.severity === 'warning').length;\n const now = RANK[repaired.validation.status];\n const was = RANK[before.validation.status];\n return now > was || (now === was && warnings(repaired) <= warnings(before));\n}\n\n/**\n * Drafts items from a source, with a bounded repair loop: asks the port with\n * `request`, reads the reply with {@link checkAiDrafts}, then — up to `repairs`\n * times — sends back the drafts with something wrong the model can fix\n * ({@link aiDraftsRepairRequest}), and keeps a repaired draft only when it is\n * no worse than the one it replaces. A repaired draft keeps its id.\n *\n * Never throws for a model's failure: a port that throws or a reply the SDK\n * refuses ends the loop, and is recorded in `calls` beside the provenance and\n * usage of every call — which the SDK never adds up. A first call that fails\n * leaves no drafts.\n *\n * ```ts\n * const run = await generateDrafts({\n * request: aiDraftsRequest({ types: ['multiple-choice', 'dictation'], source: { kind: 'transcript', text } }),\n * port: (request) => callMyModel(DRAFTS_PROMPT, request),\n * newId: () => crypto.randomUUID(),\n * });\n * // run.drafts: store each as proposed, for a person to approve.\n * ```\n *\n * @throws RangeError when `repairs` is not a whole number from 0 to\n * {@link AI_DRAFTS_MAX_REPAIRS} (default 1), and Error when `newId` returns an\n * empty or repeated id.\n */\nexport async function generateDrafts(input: {\n request: AiDraftsRequest;\n port: (request: AiDraftsRequest) => Promise<unknown> | unknown;\n newId: () => string;\n repairs?: number;\n}): Promise<AiDraftsRun> {\n const { request, port, newId } = input;\n const repairs = input.repairs ?? 1;\n if (!Number.isInteger(repairs) || repairs < 0 || repairs > AI_DRAFTS_MAX_REPAIRS) {\n throw new RangeError(\n `generateDrafts: repairs must be a whole number from 0 to ${AI_DRAFTS_MAX_REPAIRS}, not ${String(repairs)}.`,\n );\n }\n const calls: AiDraftsCall[] = [];\n // One source of ids for the whole run, so no id is issued twice across passes.\n const ids = idSource(newId, 'generateDrafts');\n\n const ask = async (\n asked: AiDraftsRequest,\n keep: readonly (string | undefined)[],\n ): Promise<AiDrafts | undefined> => {\n let raw: unknown;\n try {\n raw = await port(asked);\n } catch (error) {\n calls.push({ ok: false, error: error instanceof Error ? error.message : String(error) });\n return undefined;\n }\n const checked = check(raw, asked, ids, keep);\n if (!checked.ok) {\n calls.push({ ok: false, refusal: checked.refusal });\n return undefined;\n }\n const { provenance, usage } = checked.drafts;\n calls.push({\n ok: true,\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n });\n return checked.drafts;\n };\n\n const first = await ask(request, []);\n if (first === undefined) {\n return { drafts: [], findings: [], calls };\n }\n let current = first.drafts;\n for (let pass = 0; pass < repairs; pass += 1) {\n const repairRequest = aiDraftsRepairRequest(request, current);\n if (repairRequest === null) {\n break;\n }\n const keep = (repairRequest.repair ?? []).map((wanted) => {\n const before = current.find((one) => one.index === wanted.index);\n return typeof before?.draft.id === 'string' ? before.draft.id : undefined;\n });\n const repaired = await ask(repairRequest, keep);\n if (repaired === undefined) {\n break;\n }\n current = current.map((before) => {\n const after = repaired.drafts.find((one) => one.index === before.index);\n return after !== undefined && noWorse(after, before) ? after : before;\n });\n }\n return {\n drafts: current,\n findings: keyPositionFinding(\n current.map((one) => one.draft),\n [],\n ),\n calls,\n };\n}\n\n// ── An interactive video ───────────────────────────────────────────────\n\n/** The source as one text, kept with the video as its author-only transcript. */\nfunction sourceText(request: AiDraftsRequest): string {\n const source = request.facts.source;\n return source.kind === 'captions'\n ? source.captions.map((caption) => caption.text).join('\\n')\n : source.text;\n}\n\n/**\n * An interactive video made of drafts: an item group whose stimulus is\n * `video`, whose items are the drafts, and whose timeline opens a quiz at each\n * moment a draft was placed — drafts placed at one moment share its quiz, in\n * the author's order of types — and one more at `durationSeconds` for every\n * draft placed nowhere: all of them when the source had no times.\n *\n * The SDK never reads the video, so it cannot know where the end is: without\n * `durationSeconds` that last quiz has no time yet, which\n * `validateItemGroupDraft` reports for the author to set. The source is kept on\n * the stimulus as its author-only `transcript`.\n *\n * Like every draft it is checked (`validateItemGroupDraft`) and critiqued\n * (`critiqueItemGroupDraft`), never approved. A dictation still needs its own\n * recording; a type a video does not take — a written response — is reported,\n * not dropped.\n *\n * @throws Error when `newId` returns an empty or repeated id.\n */\nexport function interactiveVideoFromDrafts(input: {\n request: AiDraftsRequest;\n drafts: AiDrafts | AiDraftsRun | readonly AiGeneratedDraft[];\n video: ActivityMedia;\n durationSeconds?: number;\n title?: string;\n newId: () => string;\n}): AiVideoDraft {\n const { request, drafts, video, durationSeconds, title } = input;\n const ids = idSource(input.newId, 'interactiveVideoFromDrafts');\n const types = request.facts.activityTypes;\n const list: readonly AiGeneratedDraft[] = 'drafts' in drafts ? drafts.drafts : drafts;\n const end =\n typeof durationSeconds === 'number' && Number.isFinite(durationSeconds) && durationSeconds >= 0\n ? durationSeconds\n : undefined;\n // When each draft opens: its caption's end, or the end of the video.\n const placed = list.map((one) => ({ one, at: one.at ?? end }));\n const ordered = [...placed].sort(\n (a, b) =>\n (a.at ?? Number.POSITIVE_INFINITY) - (b.at ?? Number.POSITIVE_INFINITY) ||\n types.indexOf(a.one.type) - types.indexOf(b.one.type) ||\n a.one.index - b.one.index,\n );\n const cues: { id: string; at?: number; itemIds: string[] }[] = [];\n for (const { one, at } of ordered) {\n const itemId = String(one.draft.id);\n const last = cues[cues.length - 1];\n if (last !== undefined && last.at === at) {\n last.itemIds.push(itemId);\n } else {\n cues.push({ id: ids(), ...(at !== undefined ? { at } : {}), itemIds: [itemId] });\n }\n }\n const group = {\n schemaVersion: '1.0',\n type: 'item-group',\n id: ids(),\n ...(title !== undefined ? { title } : {}),\n stimulus: { id: ids(), kind: 'video', media: video, transcript: sourceText(request) },\n items: ordered.map(({ one }) => one.draft),\n timeline: { cues },\n };\n return {\n group,\n validation: validateItemGroupDraft(group),\n findings: critiqueItemGroupDraft(group),\n };\n}\n","import { cleanText, folded, needle } from './ai-text.js';\nimport type { AiRefusal } from './types/ai.js';\nimport type { InlineCorrection } from './types/grading.js';\n\n/**\n * What the AI features about a written response share: reading a model's text\n * fields, and finding each correction's quote in the learner's text.\n *\n * Internal. `ai-writing.ts` (feedback on a draft) and `ai-grading.ts` (a\n * grade) are their public faces, and hold every correction to the same\n * anchoring.\n */\n\n/** The most corrections one piece of writing feedback may carry. More is refused, not cut. */\nexport const AI_WRITING_MAX_CORRECTIONS = 20;\n\n/** The longest quote, correction, explanation or criterion comment, in code points. */\nexport const AI_WRITING_MAX_FIELD_LENGTH = 500;\n\n/** The longest `category` or `band`: a tag, not a sentence. */\nexport const MAX_TAG_LENGTH = 40;\n\n// ── Finding a quote in the draft ───────────────────────────────────────\n\n/**\n * Where `quote` sits in the draft: at `claimed`, when the model named a range —\n * which must then hold the quote — or else at its first occurrence not already\n * taken by an identical quote, so a mistake made twice is anchored twice. A\n * quote corrected more often than it occurs takes its first occurrence again.\n * `null` when the draft does not contain it where claimed, or at all.\n */\nfunction anchor(\n draft: { text: string; chars: string; from: number[] },\n quote: string,\n claimed: { start: number; end: number } | undefined,\n taken: Set<string>,\n): { start: number; end: number } | null {\n const sought = needle(quote);\n if (sought === '') {\n return null;\n }\n if (claimed !== undefined) {\n const { start, end } = claimed;\n if (\n !Number.isSafeInteger(start) ||\n !Number.isSafeInteger(end) ||\n start < 0 ||\n end > draft.text.length ||\n start >= end\n ) {\n return null;\n }\n const span = draft.text.slice(start, end);\n if (needle(span) !== sought) {\n return null;\n }\n // The span, without whitespace the model's range took in at either end.\n const lead = span.length - span.trimStart().length;\n const trail = span.length - span.trimEnd().length;\n return { start: start + lead, end: end - trail };\n }\n let first: { start: number; end: number } | null = null;\n for (let at = draft.chars.indexOf(sought); at !== -1; at = draft.chars.indexOf(sought, at + 1)) {\n const start = draft.from[at] as number;\n const end = (draft.from[at + sought.length - 1] as number) + 1;\n const found = { start, end };\n first ??= found;\n const key = `${start}:${end}`;\n if (!taken.has(key)) {\n taken.add(key);\n return found;\n }\n }\n return first;\n}\n\n// ── Checking what comes back ───────────────────────────────────────────\n\n/** A field that must be a string, at most `max` code points once cleaned; `undefined` when absent. */\nexport function textField(\n value: unknown,\n max: number,\n): { ok: true; value: string | undefined } | { ok: false; refusal: AiRefusal } {\n if (value === undefined || value === null) {\n return { ok: true, value: undefined };\n }\n if (typeof value !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n const cleaned = cleanText(value);\n if ([...cleaned].length > max) {\n return { ok: false, refusal: 'too-long' };\n }\n return { ok: true, value: cleaned === '' ? undefined : cleaned };\n}\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/** The corrections, each anchored in the draft, or why the reply is refused. */\nexport function checkCorrections(\n raw: unknown,\n text: string,\n): { ok: true; corrections: InlineCorrection[] } | { ok: false; refusal: AiRefusal } {\n if (raw === undefined || raw === null) {\n return { ok: true, corrections: [] };\n }\n if (!Array.isArray(raw)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (raw.length > AI_WRITING_MAX_CORRECTIONS) {\n return { ok: false, refusal: 'too-long' };\n }\n const draft = { text, ...folded(text) };\n const taken = new Set<string>();\n const corrections: InlineCorrection[] = [];\n for (const entry of raw) {\n if (!isRecord(entry) || typeof entry.original !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n if (typeof entry.corrected !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n if (\n [...entry.original].length > AI_WRITING_MAX_FIELD_LENGTH ||\n [...entry.corrected].length > AI_WRITING_MAX_FIELD_LENGTH\n ) {\n return { ok: false, refusal: 'too-long' };\n }\n // A correction that changes nothing corrects nothing.\n if (needle(entry.original) === needle(entry.corrected)) {\n return { ok: false, refusal: 'malformed' };\n }\n const explanation = textField(entry.explanation, AI_WRITING_MAX_FIELD_LENGTH);\n const category = textField(entry.category, MAX_TAG_LENGTH);\n if (!explanation.ok) {\n return explanation;\n }\n if (!category.ok) {\n return category;\n }\n let claimed: { start: number; end: number } | undefined;\n if (entry.range !== undefined && entry.range !== null) {\n if (!isRecord(entry.range)) {\n return { ok: false, refusal: 'malformed' };\n }\n claimed = { start: entry.range.start as number, end: entry.range.end as number };\n }\n const at = anchor(draft, entry.original, claimed, taken);\n if (at === null) {\n return { ok: false, refusal: 'misquotes-answer' };\n }\n corrections.push({\n original: text.slice(at.start, at.end),\n corrected: cleanText(entry.corrected),\n ...(explanation.value !== undefined ? { explanation: explanation.value } : {}),\n ...(category.value !== undefined ? { category: category.value } : {}),\n range: at,\n });\n }\n return { ok: true, corrections };\n}\n","import { AI_TEXT_MAX_LENGTH } from './ai.js';\nimport { materialFacts } from './ai-material.js';\nimport { provenanceOf, usageOf } from './ai-text.js';\nimport {\n AI_WRITING_MAX_FIELD_LENGTH,\n checkCorrections,\n isRecord,\n textField,\n} from './ai-written.js';\nimport { countWords } from './count-words.js';\nimport {\n LEVEL_PROBABILITY_TOLERANCE,\n levelDistribution,\n levelKey,\n readRubricLevels,\n} from './rubric-levels.js';\nimport { SPEAKING_MEASURES } from './schemas/speaking-response.js';\nimport { validateSpeechAssessment } from './scoring/speech/assessment.js';\nimport type {\n LearnerResponse,\n SpeakingMeasure,\n SpeakingResponseData,\n WrittenResponseData,\n} from './types/activity.js';\nimport type {\n AiActivityInput,\n AiGradedCriterion,\n AiGradingCriterionFact,\n AiGradingJudgement,\n AiGradingRequest,\n AiGradingRequestProblem,\n AiGroupInput,\n AiRefusal,\n AiSpeakingCriterionFact,\n AiSpeakingGradingRequest,\n AiSpeakingGradingRequestProblem,\n AiSpeakingMeasureFact,\n} from './types/ai.js';\nimport type { SpeechAssessment } from './types/speech.js';\n\n/**\n * How far a model's level probabilities may sum from 1 and still be a\n * distribution: rounding in a model's output, not a different judgement.\n */\nexport const AI_GRADING_PROBABILITY_TOLERANCE = LEVEL_PROBABILITY_TOLERANCE;\n\n/**\n * The request for a model's grade of a submitted written response, or why there\n * is none to make.\n *\n * Grading happens on the server, from the whole item: the rubric is what a\n * model is held to, so an item without one — or one whose criteria are not each\n * a name and a weight — gives no request, nor does a `redact()` projection or\n * an empty response. A criterion's `levels`, when there are any, must be ones\n * the grading functions can read; levels they cannot read give no request\n * rather than a grade on a scale the author did not mean. `group` is the item\n * group the task sits in: its material goes with the request as `material`, as\n * on feedback on writing.\n *\n * The author's AI switch (`ai.explanations`) is not read: grading is the\n * deployment's decision, made on the server, not help a learner asks for.\n */\nexport function aiGradingRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n group?: AiGroupInput;\n learnerLocale?: string;\n}):\n | { ok: true; request: AiGradingRequest }\n | { ok: false; problem: AiGradingRequestProblem; path?: (string | number)[] } {\n const { data, response, learnerLocale } = input;\n if (data.type !== 'written-response') {\n return { ok: false, problem: 'not-written-response' };\n }\n if ((data as { redacted?: unknown }).redacted === true) {\n return { ok: false, problem: 'redacted' };\n }\n const item = data as unknown as WrittenResponseData;\n const rubric = rubricFacts(item.rubric);\n if (!rubric.ok) {\n return rubric;\n }\n const text = response?.type === 'written-response' ? response.text : undefined;\n if (typeof text !== 'string' || text.trim() === '') {\n return { ok: false, problem: 'empty-response' };\n }\n const wordCount = countWords(text);\n const minWords = typeof item.minWords === 'number' ? item.minWords : 0;\n const maxWords = typeof item.maxWords === 'number' ? item.maxWords : Number.POSITIVE_INFINITY;\n const material = materialFacts(input.group);\n return {\n ok: true,\n request: {\n feature: 'grading',\n facts: {\n activityType: 'written-response',\n activityId: data.id,\n title: data.title,\n ...(typeof data.locale === 'string' ? { locale: data.locale } : {}),\n prompt: typeof item.prompt === 'string' ? item.prompt : '',\n text,\n wordCount,\n minWords,\n maxWords,\n withinWordBounds: wordCount >= minWords && wordCount <= maxWords,\n rubric: rubric.criteria,\n ...(typeof item.languageTarget === 'string' ? { languageTarget: item.languageTarget } : {}),\n },\n ...(material !== null ? { material } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n },\n };\n}\n\nconst MEASURES: readonly unknown[] = SPEAKING_MEASURES;\n\n/**\n * The request for a model's grade of a spoken answer (1.9), or why there is\n * none to make.\n *\n * Built as a written response's is, on the server and from the whole item, with\n * the take's evidence: the model reads `assessment.transcript` — the\n * transcriber's verbatim text, the same text the engine measured the take\n * against — and scores the criteria a judge scores: those that name no\n * `measure`, or every criterion when `judgeMeasures` is set. The measured\n * criteria go with it as context, with the engine's scores, so feedback can\n * speak of them; the model does not score them.\n *\n * The evidence is checked as `gradeSpeakingResponse` checks it — valid,\n * unscripted, for this item's locale and this response's recording — so a model\n * is never paid to judge a take the grade would refuse. Read the reply with\n * `checkAiGrading`, and grade with `gradeSpeakingResponse` under the same\n * `judgeMeasures`.\n */\nexport function aiSpeakingGradingRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n assessment: SpeechAssessment | null;\n judgeMeasures?: boolean;\n group?: AiGroupInput;\n learnerLocale?: string;\n}):\n | { ok: true; request: AiSpeakingGradingRequest }\n | { ok: false; problem: AiSpeakingGradingRequestProblem; path?: (string | number)[] } {\n const { data, response, learnerLocale } = input;\n if (data.type !== 'speaking-response') {\n return { ok: false, problem: 'not-speaking-response' };\n }\n if ((data as { redacted?: unknown }).redacted === true) {\n return { ok: false, problem: 'redacted' };\n }\n const item = data as unknown as SpeakingResponseData;\n const rubric = rubricFacts(item.rubric);\n if (!rubric.ok) {\n return rubric;\n }\n // `rubricFacts` read every criterion, in order, or answered with a problem.\n const authored: readonly { measure?: unknown }[] = item.rubric.criteria;\n const judged: AiSpeakingCriterionFact[] = [];\n const measured: AiSpeakingMeasureFact[] = [];\n for (const [index, fact] of rubric.criteria.entries()) {\n const measure = (authored[index] as { measure?: unknown }).measure;\n if (measure !== undefined && !MEASURES.includes(measure)) {\n return { ok: false, problem: 'no-rubric', path: ['rubric', 'criteria', index, 'measure'] };\n }\n if (measure === undefined || input.judgeMeasures === true) {\n judged.push({\n ...fact,\n ...(measure !== undefined ? { measure: measure as SpeakingMeasure } : {}),\n });\n } else {\n measured.push({ name: fact.name, measure: measure as SpeakingMeasure, weight: fact.weight });\n }\n }\n if (judged.length === 0) {\n return { ok: false, problem: 'nothing-to-judge' };\n }\n const recording = response?.type === 'speaking-response' ? response.recording : null;\n if (recording === null || recording === undefined) {\n return { ok: false, problem: 'empty-response' };\n }\n const checked =\n input.assessment === null || input.assessment === undefined\n ? undefined\n : validateSpeechAssessment(input.assessment);\n if (checked === undefined || !checked.success) {\n return { ok: false, problem: 'invalid-assessment' };\n }\n const evidence = checked.data;\n if (evidence.status === 'no_speech') {\n return { ok: false, problem: 'empty-response' };\n }\n if (\n evidence.task !== 'unscripted' ||\n evidence.locale !== item.locale ||\n evidence.recordingKey !== recording.key\n ) {\n return { ok: false, problem: 'evidence-mismatch' };\n }\n if (evidence.transcript === undefined) {\n return { ok: false, problem: 'no-transcript' };\n }\n const text = evidence.transcript.text;\n const scores = evidence.scores;\n const scoreOf = {\n pronunciation: scores.accuracy,\n fluency: scores.fluency,\n prosody: scores.prosody,\n };\n const media =\n isRecord(item.media) && typeof item.media.type === 'string' ? item.media : undefined;\n const material = materialFacts(input.group);\n return {\n ok: true,\n request: {\n feature: 'speaking-grading',\n facts: {\n activityType: 'speaking-response',\n activityId: data.id,\n title: data.title,\n locale: item.locale,\n ...(typeof item.instructions === 'string' ? { instructions: item.instructions } : {}),\n prompt: typeof item.prompt === 'string' ? item.prompt : '',\n ...(media !== undefined\n ? {\n media: {\n type: media.type,\n ...(typeof media.alt === 'string' ? { alt: media.alt } : {}),\n },\n }\n : {}),\n text,\n wordCount: countWords(text),\n rubric: judged,\n measured: measured.map((fact) => {\n const score = scoreOf[fact.measure];\n return score !== undefined ? { ...fact, score } : fact;\n }),\n ...(typeof item.languageTarget === 'string' ? { languageTarget: item.languageTarget } : {}),\n },\n ...(material !== null ? { material } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n },\n };\n}\n\n/** Why a rubric cannot be given to a model, for either kind of answer. */\ntype RubricProblem = 'no-rubric' | 'duplicate-criteria' | 'unreadable-levels';\n\n/** The rubric as a model is given it, each criterion's levels read. */\nfunction rubricFacts(\n rubric: unknown,\n):\n | { ok: true; criteria: AiGradingCriterionFact[] }\n | { ok: false; problem: RubricProblem; path?: (string | number)[] } {\n const listed = isRecord(rubric) ? rubric.criteria : undefined;\n if (!Array.isArray(listed) || listed.length === 0) {\n return { ok: false, problem: 'no-rubric' };\n }\n const criteria: AiGradingCriterionFact[] = [];\n const names = new Set<string>();\n for (const [index, criterion] of listed.entries()) {\n if (!isRecord(criterion)) {\n return { ok: false, problem: 'no-rubric', path: ['rubric', 'criteria', index] };\n }\n const { name, description, weight } = criterion;\n if (typeof name !== 'string' || name.trim() === '' || typeof weight !== 'number') {\n return { ok: false, problem: 'no-rubric', path: ['rubric', 'criteria', index] };\n }\n // A model cannot tell two criteria named alike apart, so neither can a check.\n if (names.has(levelKey(name))) {\n return {\n ok: false,\n problem: 'duplicate-criteria',\n path: ['rubric', 'criteria', index, 'name'],\n };\n }\n names.add(levelKey(name));\n const levels = readRubricLevels(criterion.levels);\n if (!levels.ok) {\n return {\n ok: false,\n problem: 'unreadable-levels',\n path: ['rubric', 'criteria', index, 'levels'],\n };\n }\n criteria.push({\n name,\n ...(typeof description === 'string' ? { description } : {}),\n weight,\n levels: levels.levels,\n });\n }\n return { ok: true, criteria };\n}\n\n// ── Checking what comes back ───────────────────────────────────────────\n\ntype Refused = { ok: false; refusal: AiRefusal };\n\n/**\n * The rubric's criteria by name, folded as level labels are: composed, trimmed,\n * spaced once, lower-cased. A model that writes \"task achievement\" for \"Task\n * achievement\" judged that criterion. The request refused a rubric with two\n * names alike once folded, so a folded name is one criterion's alone.\n */\nfunction criterionIndex(\n rubric: readonly AiGradingCriterionFact[],\n): (name: string) => number | undefined {\n const folded = new Map(rubric.map((criterion, index) => [levelKey(criterion.name), index]));\n return (name) => folded.get(levelKey(name));\n}\n\n/** A finite number. */\nconst isNumber = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value);\n\n/**\n * One criterion's judgement, on that criterion's scale: a level it has, with\n * any probabilities a distribution over its levels whose likeliest is the\n * level chosen; or, without levels, a score from 0 to its `maxScore` (1 by\n * default).\n */\nfunction judgeCriterion(\n entry: Record<string, unknown>,\n criterion: AiGradingCriterionFact,\n): { ok: true; judged: Omit<AiGradedCriterion, 'comment'> } | Refused {\n const { level, score, maxScore, probabilities } = entry;\n const levels = criterion.levels;\n if (levels === null) {\n if (level !== undefined || probabilities !== undefined) {\n return { ok: false, refusal: 'out-of-scale' };\n }\n if (score === undefined) {\n return { ok: false, refusal: 'misses-criteria' };\n }\n if (!isNumber(score) || (maxScore !== undefined && !isNumber(maxScore))) {\n return { ok: false, refusal: 'malformed' };\n }\n const max = maxScore ?? 1;\n if (!(max > 0) || score < 0 || score - max > max * 1e-9) {\n return { ok: false, refusal: 'out-of-scale' };\n }\n return { ok: true, judged: { name: criterion.name, score: Math.min(1, score / max) } };\n }\n if (score !== undefined || maxScore !== undefined) {\n return { ok: false, refusal: 'out-of-scale' };\n }\n if (level === undefined) {\n return { ok: false, refusal: 'misses-criteria' };\n }\n if (typeof level !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n const chosen = levels.findIndex((one) => levelKey(one.label) === levelKey(level));\n if (chosen === -1) {\n return { ok: false, refusal: 'out-of-scale' };\n }\n const judged: Omit<AiGradedCriterion, 'comment'> = {\n name: criterion.name,\n level: (levels[chosen] as { label: string }).label,\n score: chosen / (levels.length - 1),\n };\n if (probabilities === undefined) {\n return { ok: true, judged };\n }\n if (!Array.isArray(probabilities)) {\n return { ok: false, refusal: 'malformed' };\n }\n const values = levelDistribution(probabilities, levels.length, chosen);\n if (values === null) {\n return { ok: false, refusal: 'out-of-scale' };\n }\n return { ok: true, judged: { ...judged, probabilities: values } };\n}\n\n/**\n * Checks a grade a port returned for `request`. Whole or nothing, as every AI\n * text is: a reply the SDK doubts in any part is refused, and is no grade —\n * re-ask the model or send the response to a person; never record a 0.\n *\n * - **Every criterion of the rubric, once** (`misses-criteria`). Names match as\n * written, or folded when the folded name is one criterion's alone. Leaving\n * one out, inventing or renaming one, judging one twice or calling one not\n * applicable refuses the reply: a total renormalised over fewer criteria\n * is a grade of a different task.\n * - **On each criterion's own scale** (`out-of-scale`). With levels: a `level`\n * the criterion has, as labelled (case and spacing aside), and any\n * `probabilities` a distribution over its levels, lowest first, whose\n * likeliest is the level chosen. Without levels: a `score` from 0 to its\n * `maxScore`, 1 by default.\n * - **Every correction quotes the response** (`misquotes-answer`), anchored as\n * feedback on writing anchors it.\n * - **Text fields** — a criterion's `comment` (at most\n * `AI_WRITING_MAX_FIELD_LENGTH` code points), `feedback` and `rationale`\n * (at most `AI_TEXT_MAX_LENGTH`) — are plain text, cleaned; longer is\n * `too-long`. `requiresHumanReview` is a boolean.\n *\n * The total is not computed here: `gradeFromJudgement` computes it, as\n * `gradeFromRubric` computes every grade in the SDK.\n */\nexport function checkAiGrading(\n raw: unknown,\n request: AiGradingRequest | AiSpeakingGradingRequest,\n): { ok: true; judgement: AiGradingJudgement } | Refused {\n if (!isRecord(raw)) {\n return { ok: false, refusal: 'malformed' };\n }\n const { criteria: listed, feedback, corrections, rationale, requiresHumanReview } = raw;\n if (listed === undefined) {\n return { ok: false, refusal: 'misses-criteria' };\n }\n if (!Array.isArray(listed)) {\n return { ok: false, refusal: 'malformed' };\n }\n const rubric = request.facts.rubric;\n const indexOf = criterionIndex(rubric);\n const judged: (AiGradedCriterion | undefined)[] = rubric.map(() => undefined);\n for (const entry of listed) {\n if (!isRecord(entry)) {\n return { ok: false, refusal: 'malformed' };\n }\n // Each field read once, by name: a getter must not pass a check with one\n // value and hand on another. Not rest syntax, which a transpiler may lower\n // to a copy that reads the excluded fields again.\n const name = entry.name;\n const notApplicable = entry.notApplicable;\n const written = entry.comment;\n const scale = {\n level: entry.level,\n score: entry.score,\n maxScore: entry.maxScore,\n probabilities: entry.probabilities,\n };\n if (\n typeof name !== 'string' ||\n (notApplicable !== undefined && typeof notApplicable !== 'boolean')\n ) {\n return { ok: false, refusal: 'malformed' };\n }\n const index = indexOf(name);\n if (index === undefined || judged[index] !== undefined || notApplicable === true) {\n return { ok: false, refusal: 'misses-criteria' };\n }\n const criterion = rubric[index] as AiGradingCriterionFact;\n const scaled = judgeCriterion(scale, criterion);\n if (!scaled.ok) {\n return scaled;\n }\n const comment = textField(written, AI_WRITING_MAX_FIELD_LENGTH);\n if (!comment.ok) {\n return comment;\n }\n judged[index] = {\n ...scaled.judged,\n ...(comment.value !== undefined ? { comment: comment.value } : {}),\n };\n }\n if (judged.some((one) => one === undefined)) {\n return { ok: false, refusal: 'misses-criteria' };\n }\n const anchored = checkCorrections(corrections, request.facts.text);\n if (!anchored.ok) {\n return anchored;\n }\n const said = textField(feedback, AI_TEXT_MAX_LENGTH);\n if (!said.ok) {\n return said;\n }\n const why = textField(rationale, AI_TEXT_MAX_LENGTH);\n if (!why.ok) {\n return why;\n }\n if (requiresHumanReview !== undefined && typeof requiresHumanReview !== 'boolean') {\n return { ok: false, refusal: 'malformed' };\n }\n const provenance = provenanceOf(raw.provenance);\n const usage = usageOf(raw.usage);\n return {\n ok: true,\n judgement: {\n criteria: judged as AiGradedCriterion[],\n feedback: said.value ?? null,\n corrections: anchored.corrections,\n ...(why.value !== undefined ? { rationale: why.value } : {}),\n requiresHumanReview: requiresHumanReview === true,\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n","import { AI_MATERIAL_MAX_LENGTH } from './ai-material.js';\nimport { provenanceOf, usageOf } from './ai-text.js';\nimport { keyPositionFinding } from './authoring/critique.js';\nimport {\n critiqueDraft,\n critiqueItemGroupDraft,\n validateDraft,\n validateItemGroupDraft,\n} from './authoring/index.js';\nimport { paperFindings } from './authoring/paper.js';\nimport { transcriptOf } from './media-refs.js';\nimport { getActivityTypeDescriptor } from './registry/index.js';\nimport { PLACEHOLDER_RE } from './schemas/fill-in-the-blanks.js';\nimport { seededShuffle } from './shuffle.js';\nimport type { ActivityType } from './types/activity.js';\nimport type {\n AiDraftProblem,\n AiDraftSettings,\n AiDraftsCall,\n AiGeneratedVariant,\n AiRefusal,\n AiVariantDifficulty,\n AiVariantExemplarFacts,\n AiVariantImage,\n AiVariantLevelFacts,\n AiVariantMismatch,\n AiVariantMissing,\n AiVariantPair,\n AiVariantSkip,\n AiVariantSkipCode,\n AiVariants,\n AiVariantsRequest,\n AiVariantsRun,\n AiVariantTargets,\n} from './types/ai.js';\nimport type { ItemFinding } from './types/authoring.js';\nimport {\n AI_VARIANT_NEAR_COPY_THRESHOLD,\n inPassageOrder,\n keysOf,\n type LengthTolerance,\n lengthMisses,\n lengthTargets,\n optionSet,\n sharedAnswers,\n shownScore,\n structureMisses,\n structureTargets,\n textSimilarity,\n textsOf,\n} from './variant-rules.js';\n\nexport {\n AI_VARIANT_LENGTH_BANDS,\n AI_VARIANT_NEAR_COPY_THRESHOLD,\n type LengthTolerance,\n} from './variant-rules.js';\n\n/** The most exemplars one request may vary: a long section is sent in parts. */\nexport const AI_VARIANTS_MAX_EXEMPLARS = 20;\n\n/** The most repair passes `generateVariants` makes. */\nexport const AI_VARIANTS_MAX_REPAIRS = 3;\n\n/** The longest passage or script, in characters, an exemplar may carry into the facts. */\nexport const AI_VARIANTS_MAX_MATERIAL_LENGTH = AI_MATERIAL_MAX_LENGTH;\n\n/**\n * Every code a variant can be reported with, and its severity: a contract,\n * as the critic's codes are. `docs/ai.md` documents this table and a test\n * holds the two together.\n */\nexport const AI_VARIANT_CODE_SEVERITY = {\n variant_missing: 'invalid',\n variant_type: 'invalid',\n variant_structure: 'invalid',\n variant_length: 'warning',\n variant_near_copy: 'warning',\n variant_same_key: 'warning',\n} as const;\n\n/** The activity types the SDK writes variants of: the ones whose every field it knows. */\nconst VARIANT_TYPES: readonly string[] = [\n 'multiple-choice',\n 'fill-in-the-blanks',\n 'gap-select',\n 'dictation',\n 'read-aloud',\n 'written-response',\n] satisfies readonly ActivityType[];\n\nconst DIFFICULTIES: readonly string[] = ['easier', 'same', 'harder'];\n\ntype Fields = Record<string, unknown>;\n\nconst isRecord = (value: unknown): value is Fields =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\nconst listOf = (value: unknown): readonly unknown[] => (Array.isArray(value) ? value : []);\nconst stringOf = (value: unknown): string => (typeof value === 'string' ? value : '');\nconst written = (value: unknown): string | undefined =>\n typeof value === 'string' && value.trim() !== '' ? value : undefined;\nconst textList = (value: unknown): string[] =>\n listOf(value).filter((one): one is string => typeof one === 'string');\n/** A copy of plain content, so a variant never shares an object with its exemplar. */\nconst clone = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\nconst isRecording = (media: unknown): boolean =>\n isRecord(media) && ['audio', 'video', 'embed'].includes(stringOf(media.type));\nconst isImage = (media: unknown): boolean => isRecord(media) && media.type === 'image';\n\n// ── What a model is told: rules, and levers ───────────────────────────\n\nconst RULES = [\n 'Write one variant for each exemplar, naming its exemplar index and its type.',\n \"Keep each exemplar's targets.structure exactly: the same number of options and of right options, blanks, gaps, choices and banks, items in the same order, and speakers.\",\n \"Write every word a learner reads or hears, in new words: never the exemplar's sentences with a few words changed. Write nothing else: no ids, HTML, addresses or settings.\",\n 'Keep each text within its targets.lengths, counted in words.',\n \"Test the same skill with the same kind of thinking at the same level: a literal question stays literal, an inference stays an inference, and wrong options stay as plausible as the exemplar's.\",\n \"Give each item a right answer other than its exemplar's, and other than the answers in avoid, unless the skill tested leaves only one.\",\n \"Keep the task in instructions as the exemplar's; change its wording only where the new item needs it.\",\n 'Keep a title that is a label (Part 1) as it is; give a title that names the topic a new one that gives no answer away.',\n 'Do not repeat the flaws listed in targets.exemplarFindings.',\n 'A listening script keeps the number of speakers, with new names, and about as many turns.',\n];\n\nconst DESCRIBE_RULE =\n 'Where an exemplar shows a picture, describe a new picture in its media alt, specific enough for a person to find or draw it, and ask about that picture.';\nconst REUSE_RULE =\n 'Where an exemplar shows a picture, keep it: write new questions about the same picture, which its alt describes.';\nconst DIFFICULTY_RULE =\n 'Where targets.difficulty is easier or harder, move one step, inside the level, using targets.levers. Never write for another level.';\nconst LEVEL_FACTS_RULE =\n 'Use only grammar and vocabulary that levelFacts lists as taught, and keep away from the topics it lists.';\n\n/** The ways a type is made easier or harder: one step inside a level. */\nconst LEVERS: Readonly<Record<string, Readonly<Record<'easier' | 'harder', readonly string[]>>>> = {\n 'multiple-choice': {\n easier: [\n 'Make the wrong options clearly wrong to a learner who understood, though still plausible to one who did not.',\n 'Ask about what is said directly rather than what is implied.',\n 'Word the right option closer to the words of the text or recording.',\n ],\n harder: [\n 'Make the wrong options closer to the right one, each wrong for a reason the text or recording makes clear.',\n 'Ask for an inference rather than a fact stated directly.',\n 'Paraphrase the right option rather than repeat the words of the text or recording.',\n ],\n },\n 'fill-in-the-blanks': {\n easier: ['Give more context around each blank that points to the answer.'],\n harder: [\n 'Give less context around each blank, so the learner must know the form rather than infer it.',\n ],\n },\n 'gap-select': {\n easier: [\n 'Give more context around each gap that points to the answer.',\n 'Make the wrong choices clearly wrong in context.',\n ],\n harder: [\n 'Give less context around each gap.',\n 'Make the wrong choices closer to the right one.',\n ],\n },\n dictation: {\n easier: ['Use shorter sentences and more frequent words of the level, inside the length band.'],\n harder: [\n 'Use longer sentences and less frequent words of the level, inside the length band.',\n 'Use more connected-speech features: contractions, linking, weak forms.',\n ],\n },\n 'read-aloud': {\n easier: ['Use shorter sentences and more frequent words of the level, inside the length band.'],\n harder: [\n 'Use longer sentences and less frequent words of the level, inside the length band.',\n 'Use more words with sounds and stress the level finds hard.',\n ],\n },\n 'written-response': {\n easier: ['Make the task more specific, and supply more of the content to write about.'],\n harder: ['Make the task more open, so the learner supplies more of the content.'],\n },\n reading: {\n easier: [\n 'Ask about what the passage says directly rather than what it implies.',\n 'Use more frequent words of the level, and shorter sentences with simpler syntax.',\n ],\n harder: [\n 'Ask for inferences rather than facts stated directly.',\n 'Use less frequent words of the level, and longer sentences.',\n ],\n },\n listening: {\n easier: [\n 'Describe a slower pace in the script, and have a speaker repeat the key information.',\n 'Use more frequent words of the level.',\n ],\n harder: [\n 'Describe a natural pace in the script, and give the key information once.',\n 'Use less frequent words of the level.',\n ],\n },\n};\n\nfunction leversFor(entry: Fields, difficulty: AiVariantDifficulty): string[] | undefined {\n if (difficulty === 'same') {\n return undefined;\n }\n let kind = stringOf(entry.type);\n if (kind === 'item-group') {\n const stimulus = isRecord(entry.stimulus) ? entry.stimulus : {};\n kind = stimulus.kind === 'audio' || stimulus.kind === 'video' ? 'listening' : 'reading';\n }\n const levers = LEVERS[kind];\n return levers !== undefined ? [...levers[difficulty]] : undefined;\n}\n\n// ── The exemplar, as a model is shown it ──────────────────────────────\n\n/** A passage with its placeholders numbered in order: `{{1}}`, `{{2}}`… — how a variant writes them. */\nfunction renumbered(passage: string): string {\n let position = 0;\n return passage.replace(PLACEHOLDER_RE, () => {\n position += 1;\n return `{{${position}}}`;\n });\n}\n\nconst turnsOf = (holder: Fields): { speaker: string; text: string }[] =>\n listOf(holder.script).flatMap((turn) =>\n isRecord(turn) ? [{ speaker: stringOf(turn.speaker), text: stringOf(turn.text) }] : [],\n );\n\nconst hasScript = (holder: Fields): boolean => listOf(holder.script).some(isRecord);\n\n/** What a model is shown of a media object: a picture's description, or what a recording says. */\nfunction mediaContent(\n media: unknown,\n ownText: boolean,\n ref: string | undefined,\n): Fields | undefined {\n if (!isRecord(media)) {\n return undefined;\n }\n if (isImage(media)) {\n return { alt: stringOf(media.alt), ...(ref !== undefined ? { ref } : {}) };\n }\n if (ownText || !isRecording(media)) {\n return undefined;\n }\n if (hasScript(media)) {\n return { script: turnsOf(media) };\n }\n return typeof media.transcript === 'string' ? { transcript: media.transcript } : undefined;\n}\n\nfunction feedbackContent(holder: Fields): Fields {\n if (!isRecord(holder.feedback)) {\n return {};\n }\n const { correct, incorrect } = holder.feedback;\n return {\n feedback: {\n ...(typeof correct === 'string' ? { correct } : {}),\n ...(typeof incorrect === 'string' ? { incorrect } : {}),\n },\n };\n}\n\ntype RefOf = (path: readonly (string | number)[]) => string | undefined;\n\nfunction itemContent(item: Fields, refOf: RefOf, at: readonly (string | number)[]): Fields {\n const type = stringOf(item.type);\n const ownText = type === 'dictation' || type === 'read-aloud';\n const media = mediaContent(item.media, ownText, refOf([...at, 'media']));\n const content: Fields = {\n type,\n title: stringOf(item.title),\n ...(typeof item.instructions === 'string' ? { instructions: item.instructions } : {}),\n };\n switch (type) {\n case 'multiple-choice':\n Object.assign(content, {\n question: stringOf(item.question),\n options: listOf(item.options).map((option, index) => {\n const fields = isRecord(option) ? option : {};\n const optionMedia = mediaContent(\n fields.media,\n false,\n refOf([...at, 'options', index, 'media']),\n );\n return {\n text: stringOf(fields.text),\n isCorrect: fields.isCorrect === true,\n ...(typeof fields.feedback === 'string' ? { feedback: fields.feedback } : {}),\n ...(optionMedia !== undefined ? { media: optionMedia } : {}),\n };\n }),\n });\n break;\n case 'fill-in-the-blanks':\n Object.assign(content, {\n passage: renumbered(stringOf(item.passage)),\n blanks: inPassageOrder(item, 'blanks').map(({ value }) => {\n const blank = isRecord(value) ? value : {};\n return {\n acceptedAnswers: textList(blank.acceptedAnswers),\n ...(typeof blank.hint === 'string' ? { hint: blank.hint } : {}),\n ...(typeof blank.feedback === 'string' ? { feedback: blank.feedback } : {}),\n };\n }),\n });\n break;\n case 'gap-select': {\n const banks = listOf(item.banks).filter(isRecord);\n Object.assign(content, {\n passage: renumbered(stringOf(item.passage)),\n gaps: inPassageOrder(item, 'gaps').map(({ value }) => {\n const gap = isRecord(value) ? value : {};\n const feedback = typeof gap.feedback === 'string' ? { feedback: gap.feedback } : {};\n const bank = banks.find((one) => one.id === gap.bankId);\n if (listOf(gap.choices).length === 0 && bank !== undefined) {\n const right = listOf(bank.choices).find(\n (choice) => isRecord(choice) && choice.id === gap.correctChoiceId,\n );\n return { answer: isRecord(right) ? stringOf(right.text) : '', ...feedback };\n }\n return {\n choices: listOf(gap.choices).map((choice) => ({\n text: isRecord(choice) ? stringOf(choice.text) : '',\n isCorrect: isRecord(choice) && choice.id === gap.correctChoiceId,\n })),\n ...feedback,\n };\n }),\n ...(banks.length > 0\n ? {\n banks: banks.map((bank) => ({\n choices: listOf(bank.choices).map((choice) =>\n isRecord(choice) ? stringOf(choice.text) : '',\n ),\n })),\n }\n : {}),\n });\n break;\n }\n case 'dictation':\n Object.assign(content, {\n transcript: stringOf(item.transcript),\n ...(Array.isArray(item.acceptedTranscripts)\n ? { acceptedTranscripts: textList(item.acceptedTranscripts) }\n : {}),\n });\n break;\n case 'read-aloud':\n content.referenceText = stringOf(item.referenceText);\n break;\n default:\n content.prompt = stringOf(item.prompt);\n }\n return {\n ...content,\n ...(media !== undefined ? { media } : {}),\n ...feedbackContent(item),\n };\n}\n\nfunction entryContent(entry: Fields, refOf: RefOf): Fields {\n if (entry.type !== 'item-group') {\n return itemContent(entry, refOf, []);\n }\n const stimulus = isRecord(entry.stimulus) ? entry.stimulus : {};\n const ownText = hasScript(stimulus) || typeof stimulus.transcript === 'string';\n const media = mediaContent(stimulus.media, ownText, refOf(['stimulus', 'media']));\n return {\n type: 'item-group',\n ...(typeof entry.title === 'string' ? { title: entry.title } : {}),\n stimulus: {\n kind: stringOf(stimulus.kind),\n ...(typeof stimulus.title === 'string' ? { title: stimulus.title } : {}),\n ...(typeof stimulus.body === 'string' ? { body: stimulus.body } : {}),\n ...(hasScript(stimulus)\n ? { script: turnsOf(stimulus) }\n : typeof stimulus.transcript === 'string'\n ? { transcript: stimulus.transcript }\n : {}),\n ...(media ?? {}),\n },\n items: listOf(entry.items).map((item, index) =>\n isRecord(item) ? itemContent(item, refOf, ['items', index]) : {},\n ),\n };\n}\n\n/** Every picture an entry shows: an item's, an option's, and a group's material's. */\nfunction imageSlots(entry: Fields): { path: string[]; media: Fields }[] {\n const ofItem = (item: unknown, at: string[]): { path: string[]; media: Fields }[] => {\n if (!isRecord(item)) {\n return [];\n }\n return [\n ...(isImage(item.media) ? [{ path: [...at, 'media'], media: item.media as Fields }] : []),\n ...listOf(item.options).flatMap((option, index) =>\n isRecord(option) && isImage(option.media)\n ? [{ path: [...at, 'options', String(index), 'media'], media: option.media as Fields }]\n : [],\n ),\n ];\n };\n if (entry.type !== 'item-group') {\n return ofItem(entry, []);\n }\n const stimulus = isRecord(entry.stimulus) ? entry.stimulus : {};\n return [\n ...(isImage(stimulus.media)\n ? [{ path: ['stimulus', 'media'], media: stimulus.media as Fields }]\n : []),\n ...listOf(entry.items).flatMap((item, index) => ofItem(item, ['items', String(index)])),\n ];\n}\n\n// ── Exemplars that cannot be varied ────────────────────────────────────\n\n/** Whether a recording the item's meaning depends on says nothing the facts can carry. */\nfunction unheard(entry: Fields): boolean {\n const itemUnheard = (item: unknown): boolean => {\n if (!isRecord(item) || item.type === 'dictation' || item.type === 'read-aloud') {\n return false;\n }\n return (\n (isRecording(item.media) && transcriptOf(item.media) === null) ||\n listOf(item.options).some(\n (option) =>\n isRecord(option) && isRecording(option.media) && transcriptOf(option.media) === null,\n )\n );\n };\n if (entry.type !== 'item-group') {\n return itemUnheard(entry);\n }\n const stimulus = isRecord(entry.stimulus) ? entry.stimulus : {};\n const listening =\n stimulus.kind === 'audio' || stimulus.kind === 'video' || isRecording(stimulus.media);\n return (listening && transcriptOf(stimulus) === null) || listOf(entry.items).some(itemUnheard);\n}\n\nfunction skipOf(\n entry: Fields,\n exemplarIndex: number,\n imagePolicy: 'describe' | 'reuse',\n): AiVariantSkip | undefined {\n const skip = (code: AiVariantSkipCode, message: string): AiVariantSkip => ({\n exemplarIndex,\n code,\n message,\n });\n const group = entry.type === 'item-group';\n if (group && entry.timeline !== undefined && entry.timeline !== null) {\n return skip(\n 'variant_exemplar_timeline',\n 'An interactive video is not varied: its questions are timed to its video.',\n );\n }\n const types = group\n ? listOf(entry.items).map((item) => (isRecord(item) ? stringOf(item.type) : ''))\n : [stringOf(entry.type)];\n const unsupported = types.find(\n (type) => getActivityTypeDescriptor(type) !== undefined && !VARIANT_TYPES.includes(type),\n );\n if (unsupported !== undefined) {\n return skip(\n 'variant_exemplar_unsupported',\n `The SDK does not write variants of ${unsupported}: it does not know which of its fields are words and which are settings.`,\n );\n }\n if (\n imagePolicy === 'reuse' &&\n imageSlots(entry).some((slot) => written(slot.media.alt) === undefined)\n ) {\n return skip(\n 'variant_exemplar_unseen',\n 'A picture this depends on has no description (alt), so a model cannot write questions about it.',\n );\n }\n const validation = group\n ? validateItemGroupDraft(entry)\n : validateDraft(entry.type as ActivityType, entry);\n if (validation.status !== 'complete') {\n const first = validation.issues[0];\n return skip(\n 'variant_exemplar_incomplete',\n `This is not a finished item (${first !== undefined ? `${first.code} at ${first.path.join('.') || 'its root'}` : validation.status}), so it has no structure to copy.`,\n );\n }\n if (unheard(entry)) {\n return skip(\n 'variant_exemplar_unheard',\n 'A recording this depends on has no transcript or script, so a model cannot know what it says.',\n );\n }\n const long = textsOf(entry).find((text) => text.text.length > AI_VARIANTS_MAX_MATERIAL_LENGTH);\n if (long !== undefined) {\n return skip(\n 'variant_exemplar_too_long',\n `${long.path.join('.')} is longer than ${AI_VARIANTS_MAX_MATERIAL_LENGTH} characters. Send a shorter part.`,\n );\n }\n return undefined;\n}\n\n// ── The shape a model writes ───────────────────────────────────────────\n\nconst text = (description: string) => ({ type: 'string', description });\n\nconst SCRIPT_SHAPE = {\n type: 'array',\n minItems: 1,\n description:\n 'The recording as turns, in order: a new name for each speaker, as many speakers as the exemplar.',\n items: {\n type: 'object',\n required: ['speaker', 'text'],\n properties: { speaker: text('Who speaks.'), text: text('What they say.') },\n },\n};\n\nconst MEDIA_SHAPE = {\n type: 'object',\n description:\n \"Only where the exemplar's content has media: for a picture, its alt; for a recording, its script — or transcript where the exemplar has no script.\",\n properties: {\n alt: text('What the picture shows.'),\n transcript: text('What the recording says.'),\n script: SCRIPT_SHAPE,\n },\n};\n\nconst FEEDBACK_SHAPE = {\n type: 'object',\n description: 'Only where the exemplar has feedback: shown after answering.',\n properties: {\n correct: text('Shown when the learner passes.'),\n incorrect: text('Shown when the learner does not.'),\n },\n};\n\nconst FIELD_SHAPES: Readonly<Record<string, Fields>> = {\n title: text('A short name for the item, shown above it. Never the answer, or any part of it.'),\n instructions: text(\n 'Only where the exemplar has instructions: the same task, worded for the new item.',\n ),\n question: text('The question, as plain text.'),\n options: {\n type: 'array',\n description: \"As many as the exemplar's, as many of them right.\",\n items: {\n type: 'object',\n required: ['text', 'isCorrect'],\n properties: {\n text: text('The option as the learner reads it.'),\n isCorrect: { type: 'boolean' },\n feedback: text(\"Only where the exemplar's options have feedback.\"),\n media: MEDIA_SHAPE,\n },\n },\n },\n passage: text('The passage as plain text, with {{1}}, {{2}}, … where each gap goes, in order.'),\n blanks: {\n type: 'array',\n description: 'One per gap, in the order the gaps appear: the first is {{1}}.',\n items: {\n type: 'object',\n required: ['acceptedAnswers'],\n properties: {\n acceptedAnswers: {\n type: 'array',\n minItems: 1,\n items: { type: 'string' },\n description: 'Every answer that is right, spelled as a learner would type it.',\n },\n hint: text(\"Only where the exemplar's blank has a hint. Never gives the answer away.\"),\n feedback: text(\"Only where the exemplar's blank has feedback.\"),\n },\n },\n },\n gaps: {\n type: 'array',\n description:\n 'One per gap, in the order the gaps appear. A gap with its own choices writes `choices`, exactly one right; a gap drawing on a bank writes `answer`, the text of its right choice in that bank.',\n items: {\n type: 'object',\n properties: {\n choices: {\n type: 'array',\n items: {\n type: 'object',\n required: ['text', 'isCorrect'],\n properties: { text: text('A choice.'), isCorrect: { type: 'boolean' } },\n },\n },\n answer: text(\"The right choice's text, exactly as its bank writes it.\"),\n feedback: text(\"Only where the exemplar's gap has feedback.\"),\n },\n },\n },\n banks: {\n type: 'array',\n description: \"The shared choice lists, as many as the exemplar's, each with as many choices.\",\n items: {\n type: 'object',\n required: ['choices'],\n properties: { choices: { type: 'array', items: { type: 'string' } } },\n },\n },\n transcript: text('What the learner hears and types.'),\n acceptedTranscripts: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Only where the exemplar has them: other writings of the transcript that are right, of the same kind.',\n },\n referenceText: text('What the learner reads aloud.'),\n prompt: text(\n \"The writing task, of the same genre and purpose as the exemplar's, about something new.\",\n ),\n media: MEDIA_SHAPE,\n feedback: FEEDBACK_SHAPE,\n};\n\nconst WRITES: Readonly<Record<string, readonly string[]>> = {\n 'multiple-choice': ['title', 'instructions', 'question', 'options', 'media', 'feedback'],\n 'fill-in-the-blanks': ['title', 'instructions', 'passage', 'blanks', 'media', 'feedback'],\n 'gap-select': ['title', 'instructions', 'passage', 'gaps', 'banks', 'media', 'feedback'],\n dictation: ['title', 'transcript', 'acceptedTranscripts', 'feedback'],\n 'read-aloud': ['title', 'instructions', 'referenceText', 'feedback'],\n 'written-response': ['title', 'prompt', 'media', 'feedback'],\n};\n\nfunction itemProperties(types: readonly string[]): Fields {\n const properties: Fields = {};\n for (const type of types) {\n for (const name of WRITES[type] ?? []) {\n properties[name] = FIELD_SHAPES[name];\n }\n }\n return properties;\n}\n\n/** The types an entry holds, a group's items included. */\nconst typesIn = (entry: Fields): string[] =>\n entry.type === 'item-group'\n ? listOf(entry.items).map((item) => (isRecord(item) ? stringOf(item.type) : ''))\n : [stringOf(entry.type)];\n\nfunction replyShape(entries: readonly Fields[], exactly: number): Fields {\n const top = [...new Set(entries.map((entry) => stringOf(entry.type)))];\n const itemTypes = [\n ...new Set(entries.filter((entry) => entry.type === 'item-group').flatMap(typesIn)),\n ].filter((type) => VARIANT_TYPES.includes(type));\n const properties: Fields = {\n exemplar: {\n type: 'integer',\n minimum: 0,\n description: \"The exemplar's index, as the facts give it.\",\n },\n type: { type: 'string', enum: top, description: \"The exemplar's type.\" },\n ...itemProperties(top.filter((type) => type !== 'item-group')),\n };\n if (top.includes('item-group')) {\n properties.title = FIELD_SHAPES.title;\n properties.stimulus = {\n type: 'object',\n description: 'The new material: a passage, or what a recording says.',\n properties: {\n title: text(\"Only where the exemplar's material has a title.\"),\n body: text('The passage, as plain text.'),\n transcript: text('What the recording says, where the exemplar has no script.'),\n script: SCRIPT_SHAPE,\n alt: text('For a picture: what it shows.'),\n },\n };\n properties.items = {\n type: 'array',\n description: \"The questions about the new material: the exemplar's types, in its order.\",\n items: {\n type: 'object',\n required: ['type'],\n properties: { type: { type: 'string', enum: itemTypes }, ...itemProperties(itemTypes) },\n },\n };\n }\n return {\n type: 'object',\n required: ['variants'],\n properties: {\n variants: {\n type: 'array',\n minItems: exactly,\n maxItems: exactly,\n items: { type: 'object', required: ['exemplar', 'type'], properties },\n },\n },\n };\n}\n\n// ── The request ────────────────────────────────────────────────────────\n\nconst facing = (path: readonly string[]): string =>\n path.join('.').replace(/^stimulus\\.media\\./, 'stimulus.');\n\nfunction targetsOf(\n entry: Fields,\n difficulty: AiVariantDifficulty,\n reason: string | undefined,\n itemDifficulty: AiVariantTargets['itemDifficulty'],\n tolerance: LengthTolerance | undefined,\n): AiVariantTargets {\n const group = entry.type === 'item-group';\n const levers = leversFor(entry, difficulty);\n const findings = group\n ? critiqueItemGroupDraft(entry)\n : critiqueDraft(entry.type as ActivityType, entry);\n return {\n type: stringOf(entry.type),\n structure: structureTargets(entry),\n lengths: Object.fromEntries(\n lengthTargets(entry, tolerance).map((target) => [facing(target.path), target.bounds]),\n ),\n difficulty,\n ...(difficulty !== 'same' && reason !== undefined ? { difficultyReason: reason } : {}),\n ...(levers !== undefined ? { levers } : {}),\n ...(itemDifficulty !== undefined ? { itemDifficulty } : {}),\n exemplarFindings: findings.map((found) => ({ code: found.code, message: found.message })),\n };\n}\n\nconst idOf = (value: unknown): string | undefined =>\n isRecord(value) && typeof value.id === 'string' ? value.id : undefined;\n\n/**\n * What a model is given to write a variant of each exemplar — a parallel item:\n * the exemplar's type, structure, scoring and settings, with every word a\n * learner reads or hears new — and what `checkAiVariants` needs to check the\n * reply against.\n *\n * `exemplars` are loose activities and item groups as stored, answer keys and\n * author-only transcripts included, in paper order; one variant is written for\n * each that can be varied. An exemplar that cannot is left out of the facts\n * and listed in `skipped`: a recording with nothing to say what it says\n * (`variant_exemplar_unheard`), a picture with no description to reuse\n * (`variant_exemplar_unseen`), an unfinished item\n * (`variant_exemplar_incomplete`), an interactive video\n * (`variant_exemplar_timeline`), a type the SDK does not vary\n * (`variant_exemplar_unsupported`) or material over\n * {@link AI_VARIANTS_MAX_MATERIAL_LENGTH} characters\n * (`variant_exemplar_too_long`). When every exemplar is skipped the request has\n * none, and `generateVariants` makes no call.\n *\n * The facts give each exemplar's content in the shape a variant is written in,\n * and its `targets`: the structure to keep, the length of each text in words,\n * its difficulty with the levers to move it, and the critic's findings on it\n * not to repeat. `rules` are what every variant must respect, as sentences a\n * prompt can quote. Pictures are listed in `images`, for a host that attaches\n * them; no recording's address reaches the facts. `avoid` is the other forms'\n * items: compared with every variant, never varied.\n *\n * Nothing passed is changed.\n *\n * @throws RangeError when `exemplars` is empty or longer than\n * {@link AI_VARIANTS_MAX_EXEMPLARS}, an exemplar is neither an item group nor\n * a registered activity type, `level` is empty, a difficulty is not `easier`,\n * `same` or `harder`, `images` is neither `describe` nor `reuse`, or\n * `difficultyFor` names an id no exemplar has.\n */\nexport function aiVariantsRequest(input: {\n exemplars: readonly unknown[];\n level: string;\n difficulty?: AiVariantDifficulty;\n difficultyFor?: Readonly<Record<string, { difficulty: AiVariantDifficulty; reason?: string }>>;\n locale?: string;\n levelFacts?: AiVariantLevelFacts;\n avoid?: readonly unknown[];\n images?: 'describe' | 'reuse';\n instructions?: string;\n settings?: AiDraftSettings;\n lengthTolerance?: LengthTolerance;\n balanceKeyPositions?: { seed: string };\n}): AiVariantsRequest {\n const { exemplars, level, locale, levelFacts, avoid, instructions, settings } = input;\n const difficulty = input.difficulty ?? 'same';\n const imagePolicy = input.images ?? 'describe';\n if (\n !Array.isArray(exemplars) ||\n exemplars.length === 0 ||\n exemplars.length > AI_VARIANTS_MAX_EXEMPLARS\n ) {\n throw new RangeError(\n `aiVariantsRequest: exemplars must hold 1 to ${AI_VARIANTS_MAX_EXEMPLARS} entries; send a longer section in parts.`,\n );\n }\n if (typeof level !== 'string' || level.trim() === '') {\n throw new RangeError('aiVariantsRequest: level is required: a variant must stay inside it.');\n }\n if (!DIFFICULTIES.includes(difficulty)) {\n throw new RangeError(\n `aiVariantsRequest: difficulty must be easier, same or harder, not ${String(difficulty)}.`,\n );\n }\n if (imagePolicy !== 'describe' && imagePolicy !== 'reuse') {\n throw new RangeError(\n `aiVariantsRequest: images must be describe or reuse, not ${String(imagePolicy)}.`,\n );\n }\n exemplars.forEach((entry, index) => {\n const type = isRecord(entry) ? entry.type : undefined;\n if (\n typeof type !== 'string' ||\n (type !== 'item-group' && getActivityTypeDescriptor(type) === undefined)\n ) {\n throw new RangeError(\n `aiVariantsRequest: exemplar ${index} is neither an item group nor a registered activity type.`,\n );\n }\n });\n const entries = exemplars as readonly Fields[];\n const ids = new Set(\n entries\n .flatMap((entry) => [idOf(entry), ...listOf(entry.items).map(idOf)])\n .filter((id): id is string => id !== undefined),\n );\n for (const [id, wanted] of Object.entries(input.difficultyFor ?? {})) {\n if (!ids.has(id)) {\n throw new RangeError(\n `aiVariantsRequest: difficultyFor names \"${id}\", which no exemplar has.`,\n );\n }\n if (!isRecord(wanted) || !DIFFICULTIES.includes(wanted.difficulty as string)) {\n throw new RangeError(\n `aiVariantsRequest: difficultyFor[\"${id}\"] must be easier, same or harder.`,\n );\n }\n }\n const moved = (id: string | undefined) =>\n id !== undefined ? input.difficultyFor?.[id] : undefined;\n\n const skipped: AiVariantSkip[] = [];\n const images: AiVariantImage[] = [];\n const facts: AiVariantExemplarFacts[] = [];\n entries.forEach((entry, exemplarIndex) => {\n const skip = skipOf(entry, exemplarIndex, imagePolicy);\n if (skip !== undefined) {\n skipped.push(skip);\n return;\n }\n const refs = new Map<string, string>();\n for (const slot of imageSlots(entry)) {\n const ref = `img-${images.length + 1}`;\n refs.set(slot.path.join('.'), ref);\n images.push({\n ref,\n exemplarIndex,\n path: slot.path,\n ...(typeof slot.media.alt === 'string' ? { alt: slot.media.alt } : {}),\n url: stringOf(slot.media.url),\n });\n }\n const own = moved(idOf(entry));\n const entryDifficulty = own?.difficulty ?? difficulty;\n const items = entry.type === 'item-group' ? listOf(entry.items) : [];\n const perItem = items.map((item, at) => {\n const wanted = moved(idOf(item));\n return {\n item: at,\n difficulty: wanted?.difficulty ?? entryDifficulty,\n ...(wanted?.reason !== undefined ? { difficultyReason: wanted.reason } : {}),\n };\n });\n const itemDifficulty = perItem.some(\n (one) => one.difficulty !== entryDifficulty || one.difficultyReason !== undefined,\n )\n ? perItem.map((one) => {\n const levers = leversFor(items[one.item] as Fields, one.difficulty);\n return { ...one, ...(levers !== undefined ? { levers } : {}) };\n })\n : undefined;\n facts.push({\n exemplar: exemplarIndex,\n type: stringOf(entry.type),\n content: entryContent(entry, (path) => refs.get(path.map(String).join('.'))),\n ...(Array.isArray(entry.learningObjectives)\n ? { learningObjectives: textList(entry.learningObjectives) }\n : {}),\n targets: targetsOf(\n entry,\n entryDifficulty,\n own?.reason,\n itemDifficulty,\n input.lengthTolerance,\n ),\n });\n });\n\n const rules = [\n ...RULES,\n ...(images.length > 0 ? [imagePolicy === 'describe' ? DESCRIBE_RULE : REUSE_RULE] : []),\n ...(facts.some(\n (one) =>\n one.targets.difficulty !== 'same' ||\n (one.targets.itemDifficulty ?? []).some((item) => item.difficulty !== 'same'),\n )\n ? [DIFFICULTY_RULE]\n : []),\n ...(levelFacts !== undefined ? [LEVEL_FACTS_RULE] : []),\n ];\n const varied = facts.map((one) => entries[one.exemplar] as Fields);\n return {\n feature: 'variant-generation',\n facts: {\n exemplars: facts,\n level,\n ...(locale !== undefined\n ? { locale }\n : typeof entries[0]?.locale === 'string'\n ? { locale: entries[0].locale }\n : {}),\n difficulty,\n ...(levelFacts !== undefined ? { levelFacts: clone(levelFacts) } : {}),\n imagePolicy,\n ...(images.length > 0 ? { images } : {}),\n ...(avoid !== undefined && avoid.length > 0\n ? {\n avoid: avoid.map((entry) => ({\n texts: textsOf(entry).map((one) => one.text),\n keys: (isRecord(entry) && entry.type === 'item-group'\n ? listOf(entry.items)\n : [entry]\n ).flatMap((item) => keysOf(item).map((slot) => slot.shown)),\n })),\n }\n : {}),\n ...(instructions !== undefined ? { instructions } : {}),\n rules,\n },\n shape: replyShape(varied, varied.length),\n exemplars,\n ...(avoid !== undefined ? { avoid } : {}),\n ...(settings !== undefined ? { settings } : {}),\n ...(input.lengthTolerance !== undefined\n ? { lengthTolerance: { ...input.lengthTolerance } }\n : {}),\n ...(input.balanceKeyPositions !== undefined\n ? { balanceKeyPositions: { seed: input.balanceKeyPositions.seed } }\n : {}),\n skipped,\n };\n}\n\n// ── From a reply to a variant ──────────────────────────────────────────\n\n/** Fields every activity copies from its exemplar: what scores it, and what it tests. */\nconst COPIED_COMMON = [\n 'scoringStrategy',\n 'shuffle',\n 'passThreshold',\n 'locale',\n 'ai',\n 'learningObjectives',\n 'slotKey',\n];\n\nconst COPIED: Readonly<Record<string, readonly string[]>> = {\n 'multiple-choice': ['mode'],\n 'fill-in-the-blanks': [],\n 'gap-select': ['presentation', 'shuffleChoices'],\n dictation: ['tolerance', 'hints'],\n 'read-aloud': ['recording', 'scoring'],\n 'written-response': ['rubric', 'minWords', 'maxWords', 'languageTarget'],\n};\n\nfunction pick(from: Fields, names: readonly string[]): Fields {\n const out: Fields = {};\n for (const name of names) {\n if (from[name] !== undefined) {\n out[name] = clone(from[name]);\n }\n }\n return out;\n}\n\n/** The author's 1–5 difficulty, one step along with the variant's, inside the scale. */\nfunction steppedLevel(level: unknown, difficulty: AiVariantDifficulty): Fields {\n if (typeof level !== 'number') {\n return {};\n }\n const step = difficulty === 'easier' ? -1 : difficulty === 'harder' ? 1 : 0;\n return { difficultyLevel: Math.min(5, Math.max(1, level + step)) };\n}\n\ninterface Build {\n request: AiVariantsRequest;\n ids: () => string;\n}\n\nconst scriptFrom = (value: unknown): { speaker: string; text: string }[] =>\n listOf(value).flatMap((turn) =>\n isRecord(turn) ? [{ speaker: stringOf(turn.speaker), text: stringOf(turn.text) }] : [],\n );\n\n/**\n * A variant's media in place of the exemplar's: a picture described anew, or\n * kept with `reuse`; a recording with no file yet — the host records it — its\n * playback policy, and the words the model wrote for it. An address, captions,\n * tracks and a poster belong to the old recording and are not copied.\n */\nfunction variantMedia(\n exemplar: Fields,\n wrote: unknown,\n ownText: boolean,\n imagePolicy: 'describe' | 'reuse',\n): Fields {\n const model = isRecord(wrote) ? wrote : {};\n if (isImage(exemplar)) {\n return imagePolicy === 'reuse'\n ? pick(exemplar, ['type', 'url', 'alt'])\n : { type: 'image', url: null, alt: stringOf(model.alt) };\n }\n const media: Fields = { type: exemplar.type, url: null, ...pick(exemplar, ['playback']) };\n if (!ownText) {\n if (hasScript(exemplar)) {\n media.script = scriptFrom(model.script);\n } else if (typeof exemplar.transcript === 'string') {\n media.transcript = stringOf(model.transcript);\n }\n }\n return media;\n}\n\n/** The feedback the model wrote, on the keys the exemplar's feedback has. */\nfunction feedbackFrom(exemplar: Fields, wrote: Fields): Fields {\n if (!isRecord(exemplar.feedback) || !isRecord(wrote.feedback)) {\n return {};\n }\n const out: Fields = {};\n for (const name of ['correct', 'incorrect']) {\n const said = written(wrote.feedback[name]);\n if (exemplar.feedback[name] !== undefined && said !== undefined) {\n out[name] = said;\n }\n }\n return Object.keys(out).length > 0 ? { feedback: out } : {};\n}\n\nfunction difficultyFor(\n request: AiVariantsRequest,\n exemplarIndex: number,\n item?: number,\n): AiVariantDifficulty {\n const targets = request.facts.exemplars.find((one) => one.exemplar === exemplarIndex)?.targets;\n const own =\n item !== undefined ? targets?.itemDifficulty?.find((one) => one.item === item) : undefined;\n return own?.difficulty ?? targets?.difficulty ?? request.facts.difficulty;\n}\n\nfunction buildActivity(\n exemplar: Fields,\n wrote: Fields,\n type: string,\n build: Build,\n difficulty: AiVariantDifficulty,\n id: string,\n): Fields {\n const { request, ids } = build;\n const imagePolicy = request.facts.imagePolicy;\n const alike = exemplar.type === type;\n const content: Fields = { title: stringOf(wrote.title) };\n if (typeof exemplar.instructions === 'string') {\n content.instructions = written(wrote.instructions) ?? exemplar.instructions;\n }\n switch (type) {\n case 'multiple-choice': {\n const before = listOf(exemplar.options);\n const withFeedback = before.some(\n (option) => isRecord(option) && option.feedback !== undefined,\n );\n content.question = stringOf(wrote.question);\n content.options = listOf(wrote.options).map((option, index) => {\n const fields = isRecord(option) ? option : {};\n const was = before[index];\n const feedback = withFeedback ? written(fields.feedback) : undefined;\n return {\n id: ids(),\n text: stringOf(fields.text),\n isCorrect: fields.isCorrect === true,\n ...(feedback !== undefined ? { feedback } : {}),\n ...(isRecord(was) && isRecord(was.media)\n ? { media: variantMedia(was.media, fields.media, false, imagePolicy) }\n : {}),\n };\n });\n break;\n }\n case 'fill-in-the-blanks': {\n const before = alike ? inPassageOrder(exemplar, 'blanks').map((one) => one.value) : [];\n content.passage = stringOf(wrote.passage);\n content.blanks = listOf(wrote.blanks).map((blank, index) => {\n const fields = isRecord(blank) ? blank : {};\n const was = isRecord(before[index]) ? (before[index] as Fields) : {};\n const hint = was.hint !== undefined ? written(fields.hint) : undefined;\n const feedback = was.feedback !== undefined ? written(fields.feedback) : undefined;\n return {\n id: String(index + 1),\n acceptedAnswers: textList(fields.acceptedAnswers),\n ...(hint !== undefined ? { hint } : {}),\n ...(feedback !== undefined ? { feedback } : {}),\n ...pick(was, ['match', 'caseSensitive', 'trimWhitespace']),\n };\n });\n break;\n }\n case 'gap-select': {\n const beforeGaps = alike ? inPassageOrder(exemplar, 'gaps').map((one) => one.value) : [];\n const beforeBanks = alike ? listOf(exemplar.banks) : [];\n const banks = beforeBanks.length\n ? listOf(wrote.banks).map((bank) => ({\n id: ids(),\n choices: listOf(isRecord(bank) ? bank.choices : undefined).map((choice) => ({\n id: ids(),\n text:\n typeof choice === 'string' ? choice : isRecord(choice) ? stringOf(choice.text) : '',\n })),\n }))\n : [];\n content.passage = stringOf(wrote.passage);\n content.gaps = listOf(wrote.gaps).map((gap, index) => {\n const fields = isRecord(gap) ? gap : {};\n const was = isRecord(beforeGaps[index]) ? (beforeGaps[index] as Fields) : {};\n const feedback = was.feedback !== undefined ? written(fields.feedback) : undefined;\n const drawsOn =\n listOf(was.choices).length === 0\n ? beforeBanks.findIndex((bank) => isRecord(bank) && bank.id === was.bankId)\n : -1;\n const bank = drawsOn !== -1 ? banks[drawsOn] : undefined;\n if (bank !== undefined) {\n const answer = stringOf(fields.answer).trim().toLowerCase();\n const right = bank.choices.find((choice) => choice.text.trim().toLowerCase() === answer);\n return {\n id: String(index + 1),\n bankId: bank.id,\n correctChoiceId: answer !== '' && right !== undefined ? right.id : '',\n ...(feedback !== undefined ? { feedback } : {}),\n };\n }\n const marked = listOf(fields.choices);\n const choices = marked.map((choice) => ({\n id: ids(),\n text: isRecord(choice) ? stringOf(choice.text) : '',\n }));\n // Exactly one marked right is a key; none, or several, is no key —\n // which validateDraft asks for, and a repair then supplies.\n const right = marked.flatMap((choice, at) =>\n isRecord(choice) && choice.isCorrect === true ? [at] : [],\n );\n return {\n id: String(index + 1),\n choices,\n correctChoiceId: right.length === 1 ? (choices[right[0] as number]?.id as string) : '',\n ...(feedback !== undefined ? { feedback } : {}),\n };\n });\n if (banks.length > 0) {\n content.banks = banks;\n }\n break;\n }\n case 'dictation':\n content.transcript = stringOf(wrote.transcript);\n if (Array.isArray(exemplar.acceptedTranscripts) && Array.isArray(wrote.acceptedTranscripts)) {\n const accepted = textList(wrote.acceptedTranscripts).filter((one) => one.trim() !== '');\n if (accepted.length > 0) {\n content.acceptedTranscripts = accepted;\n }\n }\n break;\n case 'read-aloud':\n content.referenceText = stringOf(wrote.referenceText);\n break;\n default:\n content.prompt = stringOf(wrote.prompt);\n }\n const ownText = type === 'dictation' || type === 'read-aloud';\n return {\n ...pick(exemplar, alike ? [...COPIED_COMMON, ...(COPIED[type] ?? [])] : COPIED_COMMON),\n ...steppedLevel(exemplar.difficultyLevel, difficulty),\n ...request.settings?.[type as keyof AiDraftSettings],\n ...content,\n ...(isRecord(exemplar.media)\n ? { media: variantMedia(exemplar.media, wrote.media, ownText, imagePolicy) }\n : {}),\n ...(ownText && isRecord(exemplar.slowMedia) ? { slowMedia: { type: 'audio', url: null } } : {}),\n ...feedbackFrom(exemplar, wrote),\n schemaVersion: typeof exemplar.schemaVersion === 'string' ? exemplar.schemaVersion : '1.0',\n type,\n id,\n };\n}\n\nfunction buildStimulus(exemplar: Fields, wrote: unknown, build: Build): Fields {\n const model = isRecord(wrote) ? wrote : {};\n const ownText = hasScript(exemplar) || typeof exemplar.transcript === 'string';\n const title = typeof exemplar.title === 'string' ? written(model.title) : undefined;\n return {\n id: build.ids(),\n kind: exemplar.kind,\n ...(title !== undefined ? { title } : {}),\n ...(typeof exemplar.body === 'string' ? { body: stringOf(model.body) } : {}),\n ...(hasScript(exemplar)\n ? { script: scriptFrom(model.script) }\n : typeof exemplar.transcript === 'string'\n ? { transcript: stringOf(model.transcript) }\n : {}),\n ...(isRecord(exemplar.media)\n ? { media: variantMedia(exemplar.media, model, ownText, build.request.facts.imagePolicy) }\n : {}),\n ...pick(exemplar, ['locale']),\n };\n}\n\nfunction buildEntry(\n exemplar: Fields,\n exemplarIndex: number,\n wrote: Fields,\n build: Build,\n id: string,\n): Fields {\n const difficulty = difficultyFor(build.request, exemplarIndex);\n if (exemplar.type !== 'item-group') {\n return buildActivity(exemplar, wrote, stringOf(exemplar.type), build, difficulty, id);\n }\n const before = listOf(exemplar.items);\n const title =\n typeof exemplar.title === 'string' ? (written(wrote.title) ?? exemplar.title) : undefined;\n return {\n ...pick(exemplar, ['slotKey', 'shuffle']),\n ...(title !== undefined ? { title } : {}),\n stimulus: buildStimulus(\n isRecord(exemplar.stimulus) ? exemplar.stimulus : {},\n wrote.stimulus,\n build,\n ),\n items: listOf(wrote.items).map((item, index) => {\n const fields = isRecord(item) ? item : {};\n const was = isRecord(before[index]) ? (before[index] as Fields) : {};\n const type = VARIANT_TYPES.includes(stringOf(fields.type))\n ? stringOf(fields.type)\n : stringOf(was.type);\n return buildActivity(\n was,\n fields,\n type,\n build,\n difficultyFor(build.request, exemplarIndex, index),\n build.ids(),\n );\n }),\n schemaVersion: typeof exemplar.schemaVersion === 'string' ? exemplar.schemaVersion : '1.0',\n type: 'item-group',\n id,\n };\n}\n\n// ── Checking a variant ─────────────────────────────────────────────────\n\nfunction variantFinding(\n code: 'variant_length' | 'variant_near_copy' | 'variant_same_key',\n path: readonly string[],\n message: string,\n): ItemFinding {\n return { path: [...path], message, code, severity: AI_VARIANT_CODE_SEVERITY[code] };\n}\n\n/** The items of an entry with their paths: the entry itself, or a group's items. */\nconst itemsWithPaths = (entry: unknown): { path: string[]; item: unknown }[] =>\n isRecord(entry) && entry.type === 'item-group'\n ? listOf(entry.items).map((item, index) => ({ path: ['items', String(index)], item }))\n : [{ path: [], item: entry }];\n\n/** The other forms' entries in this exemplar's place: the same `slotKey`, or the same position in each form. */\nfunction avoidedFor(\n request: AiVariantsRequest,\n exemplarIndex: number,\n): { entry: unknown; label: string }[] {\n const avoid = request.avoid ?? [];\n const exemplar = request.exemplars[exemplarIndex];\n const slotKey = isRecord(exemplar) ? exemplar.slotKey : undefined;\n const forms = avoid.length % request.exemplars.length === 0;\n return avoid.flatMap((entry, index) => {\n const theirs = isRecord(entry) ? entry.slotKey : undefined;\n const paired =\n slotKey !== undefined && theirs !== undefined\n ? slotKey === theirs\n : slotKey === undefined &&\n theirs === undefined &&\n forms &&\n index % request.exemplars.length === exemplarIndex;\n return paired ? [{ entry, label: `avoid[${index}]` }] : [];\n });\n}\n\nfunction nearCopies(entry: Fields, exemplar: Fields, request: AiVariantsRequest): ItemFinding[] {\n const mine = new Map(textsOf(exemplar).map((one) => [one.path.join('.'), one]));\n const avoided = (request.avoid ?? []).flatMap((other, index) =>\n textsOf(other).map((one) => ({ ...one, label: `avoid[${index}]` })),\n );\n const findings: ItemFinding[] = [];\n for (const one of textsOf(entry)) {\n let best: { score: number; what: string } | undefined;\n const consider = (score: number, what: string) => {\n if (score >= AI_VARIANT_NEAR_COPY_THRESHOLD && (best === undefined || score > best.score)) {\n best = { score, what };\n }\n };\n const theirs = mine.get(one.path.join('.'));\n if (theirs !== undefined) {\n consider(textSimilarity(theirs.text, one.text), `the exemplar's ${theirs.path.join('.')}`);\n }\n for (const other of avoided) {\n if (other.kind === one.kind) {\n consider(\n textSimilarity(other.text, one.text),\n `${other.label} ${other.path.join('.') || 'text'}`,\n );\n }\n }\n if (best !== undefined) {\n findings.push(\n variantFinding(\n 'variant_near_copy',\n one.path,\n `This reads like ${best.what} (similarity ${shownScore(best.score)}). Write it in new words.`,\n ),\n );\n }\n }\n const exemplarItems = new Map(\n itemsWithPaths(exemplar).map((one) => [one.path.join('.'), one.item]),\n );\n const avoidedOptions = (request.avoid ?? []).flatMap((other, index) =>\n itemsWithPaths(other).map((one) => ({ set: optionSet(one.item), label: `avoid[${index}]` })),\n );\n for (const { path, item } of itemsWithPaths(entry)) {\n const options = optionSet(item);\n if (options === null) {\n continue;\n }\n const sameAs = (other: string[] | null) =>\n other !== null && other.join('\\n') === options.join('\\n');\n const source = sameAs(optionSet(exemplarItems.get(path.join('.'))))\n ? \"the exemplar's\"\n : avoidedOptions.find((other) => sameAs(other.set))?.label;\n if (source !== undefined) {\n findings.push(\n variantFinding(\n 'variant_near_copy',\n [...path, 'options'],\n `These are ${source === \"the exemplar's\" ? \"the exemplar's options\" : `${source}'s options`}, in another order. Write new options.`,\n ),\n );\n }\n }\n return findings;\n}\n\nfunction sameKeys(\n entry: Fields,\n exemplar: Fields,\n exemplarIndex: number,\n request: AiVariantsRequest,\n): ItemFinding[] {\n const against = [\n { entry: exemplar as unknown, label: 'the exemplar' },\n ...avoidedFor(request, exemplarIndex),\n ];\n const findings: ItemFinding[] = [];\n for (const { path, item } of itemsWithPaths(entry)) {\n const slots = keysOf(item);\n const reported = new Set<string>();\n for (const other of against) {\n const theirs = itemsWithPaths(other.entry).find(\n (one) => one.path.join('.') === path.join('.'),\n );\n const theirSlots = keysOf(theirs?.item);\n for (const slot of slots) {\n const match = theirSlots.find((one) => one.slot === slot.slot);\n const shared = match !== undefined ? sharedAnswers(slot, match) : [];\n if (shared.length > 0 && !reported.has(slot.slot)) {\n reported.add(slot.slot);\n findings.push(\n variantFinding(\n 'variant_same_key',\n [...path, ...slot.path],\n `The right answer \"${shared[0]}\" is ${other.label === 'the exemplar' ? \"the exemplar's\" : `${other.label}'s`} too: a learner who sat that form scores again by memory.`,\n ),\n );\n }\n }\n }\n }\n return findings;\n}\n\n/** Whether a recording the exemplar's item has words for came back with none. */\nfunction wordlessRecordings(exemplar: Fields, entry: Fields): AiVariantMismatch[] {\n const mismatch = (path: string[], message: string): AiVariantMismatch => ({\n path,\n message,\n code: 'variant_structure',\n severity: 'invalid',\n });\n const out: AiVariantMismatch[] = [];\n const pairsOf = (a: unknown, b: unknown, at: string[]) => {\n if (!isRecord(a) || !isRecord(b) || a.type === 'dictation' || a.type === 'read-aloud') {\n return;\n }\n if (isRecording(a.media) && transcriptOf(a.media) !== null && transcriptOf(b.media) === null) {\n out.push(\n mismatch(\n [...at, 'media', hasScript(a.media as Fields) ? 'script' : 'transcript'],\n 'The recording has no words to record: write what it says.',\n ),\n );\n }\n };\n if (exemplar.type === 'item-group') {\n const before = isRecord(exemplar.stimulus) ? exemplar.stimulus : {};\n const after = isRecord(entry.stimulus) ? entry.stimulus : {};\n if (transcriptOf(before) !== null && transcriptOf(after) === null) {\n out.push(\n mismatch(\n [\n 'stimulus',\n hasScript(before) || (isRecord(before.media) && hasScript(before.media))\n ? 'script'\n : 'transcript',\n ],\n 'The recording has no words to record: write what it says.',\n ),\n );\n }\n listOf(exemplar.items).forEach((item, index) => {\n pairsOf(item, listOf(entry.items)[index], ['items', String(index)]);\n });\n } else {\n pairsOf(exemplar, entry, []);\n }\n return out;\n}\n\nfunction pairsOf(exemplar: Fields, entry: Fields): AiVariantPair[] {\n const top: AiVariantPair = {\n exemplarPath: [],\n variantPath: [],\n exemplarId: stringOf(exemplar.id),\n variantId: stringOf(entry.id),\n };\n if (exemplar.type !== 'item-group') {\n return [top];\n }\n const theirs = listOf(entry.items);\n return [\n top,\n ...listOf(exemplar.items).flatMap((item, index) =>\n index < theirs.length\n ? [\n {\n exemplarPath: ['items', String(index)],\n variantPath: ['items', String(index)],\n exemplarId: idOf(item) ?? '',\n variantId: idOf(theirs[index]) ?? '',\n },\n ]\n : [],\n ),\n ];\n}\n\ninterface Built {\n exemplarIndex: number;\n wroteType: string;\n entry: Fields;\n generated: Fields;\n}\n\nfunction evaluate(built: Built, request: AiVariantsRequest): AiGeneratedVariant {\n const exemplar = request.exemplars[built.exemplarIndex] as Fields;\n const type = stringOf(exemplar.type);\n const { entry } = built;\n const group = type === 'item-group';\n const validation = group\n ? validateItemGroupDraft(entry)\n : validateDraft(type as ActivityType, entry);\n const mismatches: AiVariantMismatch[] =\n built.wroteType !== type\n ? [\n {\n path: ['type'],\n message: `This was written as ${built.wroteType || 'no type'}; the exemplar is ${type}.`,\n code: 'variant_type',\n severity: 'invalid',\n },\n ]\n : [\n ...structureMisses(exemplar, entry).map(\n (miss): AiVariantMismatch => ({\n ...miss,\n code: 'variant_structure',\n severity: 'invalid',\n }),\n ),\n ...wordlessRecordings(exemplar, entry),\n ];\n const findings = [\n ...(group ? critiqueItemGroupDraft(entry) : critiqueDraft(type as ActivityType, entry)),\n ...lengthMisses(exemplar, entry, request.lengthTolerance).map((miss) =>\n variantFinding(\n 'variant_length',\n miss.path,\n `${miss.words} words; the exemplar's has ${miss.exemplarWords}, so ${miss.bounds[0]} to ${miss.bounds[1]}.`,\n ),\n ),\n ...nearCopies(entry, exemplar, request),\n ...sameKeys(entry, exemplar, built.exemplarIndex, request),\n ];\n const status =\n validation.status === 'invalid' || mismatches.length > 0\n ? 'invalid'\n : validation.status === 'incomplete'\n ? 'incomplete'\n : 'complete';\n return {\n exemplarIndex: built.exemplarIndex,\n type,\n entry,\n status,\n validation,\n mismatches,\n findings,\n pairs: pairsOf(exemplar, entry),\n generated: built.generated,\n };\n}\n\n// ── Keys spread across positions ───────────────────────────────────────\n\n/**\n * Moves each single-answer, unshuffled multiple choice's right option to the\n * position used least so far across the variants, ties broken by an order\n * drawn from `seed`: as even as the counts allow, and the same every time.\n * An option moves with its id, text, feedback and media.\n */\nfunction balanceKeys(built: readonly Built[], seed: string): Built[] {\n const counts = new Map<number, number>();\n let slot = 0;\n const visit = (item: unknown): unknown => {\n if (\n !isRecord(item) ||\n item.type !== 'multiple-choice' ||\n item.mode !== 'single' ||\n item.shuffle === true\n ) {\n return item;\n }\n const options = listOf(item.options);\n const keys = options.flatMap((option, index) =>\n isRecord(option) && option.isCorrect === true ? [index] : [],\n );\n if (keys.length !== 1) {\n return item;\n }\n const order = seededShuffle(\n options.map((_, index) => index),\n `${seed}:${slot}`,\n { version: 2 },\n );\n slot += 1;\n let target = order[0] as number;\n for (const position of order) {\n if ((counts.get(position) ?? 0) < (counts.get(target) ?? 0)) {\n target = position;\n }\n }\n counts.set(target, (counts.get(target) ?? 0) + 1);\n const key = options[keys[0] as number];\n const rest = options.filter((_, index) => index !== keys[0]);\n return { ...item, options: [...rest.slice(0, target), key, ...rest.slice(target)] };\n };\n return [...built]\n .sort((a, b) => a.exemplarIndex - b.exemplarIndex)\n .map((one) => ({\n ...one,\n entry:\n one.entry.type === 'item-group'\n ? { ...one.entry, items: listOf(one.entry.items).map(visit) }\n : (visit(one.entry) as Fields),\n }));\n}\n\n// ── Reading a reply ────────────────────────────────────────────────────\n\n/** Ids from the host, checked as `createDraft` checks them. */\nfunction idSource(newId: () => string, caller: string): () => string {\n const issued = new Set<string>();\n return () => {\n const id: unknown = newId();\n if (typeof id !== 'string' || id === '') {\n throw new Error(`${caller}: newId() must return a non-empty string.`);\n }\n if (issued.has(id)) {\n throw new Error(\n `${caller}: newId() returned \"${id}\" twice. Ids must differ — two options sharing an id cannot be scored apart.`,\n );\n }\n issued.add(id);\n return id;\n };\n}\n\ntype Read =\n | { ok: false; refusal: AiRefusal }\n | { ok: true; built: Built[]; missing: AiVariantMissing[]; raw: Fields };\n\nfunction missingOf(exemplarIndex: number): AiVariantMissing {\n return {\n exemplarIndex,\n code: 'variant_missing',\n message: `The reply has no variant of exemplar ${exemplarIndex}.`,\n severity: 'invalid',\n };\n}\n\nfunction read(\n raw: unknown,\n request: AiVariantsRequest,\n ids: () => string,\n keep: ReadonlyMap<number, string>,\n): Read {\n if (!isRecord(raw) || !Array.isArray(raw.variants) || !raw.variants.every(isRecord)) {\n return { ok: false, refusal: 'malformed' };\n }\n const asked = request.repair\n ? request.repair.map((one) => one.exemplar)\n : request.facts.exemplars.map((one) => one.exemplar);\n if (raw.variants.length > asked.length) {\n return { ok: false, refusal: 'too-long' };\n }\n const seen = new Set<number>();\n for (const variant of raw.variants as Fields[]) {\n const index = variant.exemplar;\n if (typeof index !== 'number' || !asked.includes(index) || seen.has(index)) {\n return { ok: false, refusal: 'malformed' };\n }\n seen.add(index);\n }\n const build: Build = { request, ids };\n const built = (raw.variants as Fields[]).map((variant): Built => {\n const exemplarIndex = variant.exemplar as number;\n const exemplar = request.exemplars[exemplarIndex] as Fields;\n return {\n exemplarIndex,\n wroteType: stringOf(variant.type),\n entry: buildEntry(exemplar, exemplarIndex, variant, build, keep.get(exemplarIndex) ?? ids()),\n generated: variant,\n };\n });\n return {\n ok: true,\n built: built.sort((a, b) => a.exemplarIndex - b.exemplarIndex),\n missing: asked\n .filter((index) => !seen.has(index))\n .sort((a, b) => a - b)\n .map(missingOf),\n raw,\n };\n}\n\nfunction setFindings(variants: readonly AiGeneratedVariant[]): ItemFinding[] {\n const entries = variants.map((one) => one.entry);\n return [...keyPositionFinding(entries, []), ...paperFindings(entries)];\n}\n\n/**\n * Reads a model's reply to {@link aiVariantsRequest} into variants an editor\n * can show for approval: each made from what the model wrote and what its\n * exemplar is — type, structure, scoring and settings copied from the\n * exemplar, whatever the reply says of them — with ids from `newId`, checked\n * by `validateDraft` (or `validateItemGroupDraft`), compared with its exemplar\n * (`mismatches`: `variant_type`, `variant_structure`), critiqued, and checked\n * for length, near copies and repeated keys against the exemplar and every\n * entry in `avoid` (`findings`: `variant_length`, `variant_near_copy`,\n * `variant_same_key`). `pairs` line each exemplar item up with its variant's.\n *\n * A recording comes back with no file — `url: null`, its playback policy, and\n * a new script or transcript — and a picture to describe with no file and a\n * new `alt`: `media_url_required` until the host attaches one.\n *\n * Variants are independent: one that is wrong comes back as such, beside the\n * others. An exemplar the reply wrote nothing for is listed in `missing`. The\n * whole reply is refused only when it is not `{ variants: [...] }` of objects\n * each naming an exemplar of the facts once (`malformed`), or holds more\n * variants than exemplars (`too-long`).\n *\n * With `balanceKeyPositions` the single-answer keys are spread across\n * positions, from its seed.\n *\n * @throws Error when `newId` returns an empty or repeated id.\n */\nexport function checkAiVariants(\n raw: unknown,\n request: AiVariantsRequest,\n options: { newId: () => string },\n): { ok: true; variants: AiVariants } | { ok: false; refusal: AiRefusal } {\n const result = read(raw, request, idSource(options.newId, 'checkAiVariants'), new Map());\n if (!result.ok) {\n return result;\n }\n const seed = request.balanceKeyPositions?.seed;\n const built = seed !== undefined ? balanceKeys(result.built, seed) : result.built;\n const variants = built.map((one) => evaluate(one, request));\n const provenance = provenanceOf(result.raw.provenance);\n const usage = usageOf(result.raw.usage);\n return {\n ok: true,\n variants: {\n variants,\n missing: result.missing,\n findings: setFindings(variants),\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n\n// ── Repair ─────────────────────────────────────────────────────────────\n\n/** The fields a model writes, and inside media and material, the ones it writes there. */\nconst WRITTEN_FIELDS = new Set([\n 'type',\n 'title',\n 'instructions',\n 'question',\n 'options',\n 'passage',\n 'blanks',\n 'gaps',\n 'banks',\n 'transcript',\n 'acceptedTranscripts',\n 'referenceText',\n 'prompt',\n 'feedback',\n 'media',\n 'stimulus',\n 'items',\n]);\nconst WRITTEN_IN_MEDIA = new Set(['alt', 'transcript', 'script']);\nconst WRITTEN_IN_STIMULUS = new Set(['title', 'body', 'transcript', 'script', 'media', 'alt']);\n\nfunction writable(path: readonly string[]): boolean {\n const rest = path[0] === 'items' && path.length > 2 ? path.slice(2) : path;\n const [first, second, third] = rest;\n if (first === undefined || !WRITTEN_FIELDS.has(first)) {\n return false;\n }\n if (first === 'media') {\n return second !== undefined && WRITTEN_IN_MEDIA.has(second);\n }\n if (first === 'stimulus') {\n if (second === 'media') {\n return third !== undefined && WRITTEN_IN_MEDIA.has(third);\n }\n return second !== undefined && WRITTEN_IN_STIMULUS.has(second);\n }\n if (first === 'options' && rest[2] === 'media') {\n return rest[3] !== undefined && WRITTEN_IN_MEDIA.has(rest[3]);\n }\n return true;\n}\n\n/** Where the model wrote what a problem names. */\nfunction pathForModel(variant: AiGeneratedVariant, problem: AiDraftProblem): string[] {\n const { path, code } = problem;\n const prefix = path[0] === 'items' && path.length > 2 ? path.slice(0, 2) : [];\n const rest = path.slice(prefix.length);\n if (code === 'ra_text_long_for_time') {\n return [...prefix, 'referenceText'];\n }\n if (rest[0] === 'gaps' && rest.length === 3 && rest[2] === 'correctChoiceId') {\n const item = prefix.length > 0 ? listOf(variant.entry.items)[Number(prefix[1])] : variant.entry;\n const gap = isRecord(item) ? listOf(item.gaps)[Number(rest[1])] : undefined;\n return [\n ...prefix,\n 'gaps',\n rest[1] as string,\n isRecord(gap) && gap.bankId !== undefined ? 'answer' : 'choices',\n ];\n }\n if (\n rest[0] === 'stimulus' &&\n rest[1] === 'media' &&\n (rest[2] === 'script' || rest[2] === 'transcript')\n ) {\n return ['stimulus', rest[2]];\n }\n return [...path];\n}\n\nfunction problemsOf(variant: AiGeneratedVariant): AiDraftProblem[] {\n return [\n ...variant.mismatches.map(\n (found): AiDraftProblem => ({\n path: found.path,\n code: found.code,\n message: found.message,\n severity: 'invalid',\n }),\n ),\n ...variant.validation.issues\n .filter((found) => found.code !== 'media_url_required')\n .map(\n (found): AiDraftProblem => ({\n path: found.path,\n code: found.code,\n message: found.message,\n severity: found.severity,\n }),\n ),\n ...variant.findings\n .filter((found) => found.severity === 'warning')\n .map(\n (found): AiDraftProblem => ({\n path: found.path,\n code: found.code,\n message: found.message,\n severity: 'warning',\n }),\n ),\n ]\n .map((problem) => ({ ...problem, path: pathForModel(variant, problem) }))\n .filter((problem) => writable(problem.path));\n}\n\n/**\n * A second request, for the variants with something the model can fix —\n * a wrong type or structure, an issue other than a missing file, or a warning\n * at a field the model writes — and the exemplars the reply wrote nothing for\n * (`variant_missing`). Each comes with its problems at the paths the model\n * wrote; the exemplar and its targets stay in the facts. `null` when there is\n * nothing to repair.\n *\n * The reply holds one variant for each, naming its exemplar;\n * {@link checkAiVariants} reads it.\n */\nexport function aiVariantsRepairRequest(\n request: AiVariantsRequest,\n variants: AiVariants | AiVariantsRun | readonly AiGeneratedVariant[],\n): AiVariantsRequest | null {\n const list: readonly AiGeneratedVariant[] = Array.isArray(variants)\n ? (variants as readonly AiGeneratedVariant[])\n : (variants as AiVariants).variants;\n const missing: readonly AiVariantMissing[] = Array.isArray(variants)\n ? []\n : (variants as AiVariants).missing;\n const repair = [\n ...list.flatMap((one) => {\n const problems = problemsOf(one);\n return problems.length === 0\n ? []\n : [{ exemplar: one.exemplarIndex, variant: one.generated, problems }];\n }),\n ...missing.map((one) => ({\n exemplar: one.exemplarIndex,\n problems: [{ path: [], code: one.code, message: one.message, severity: 'invalid' as const }],\n })),\n ].sort((a, b) => a.exemplar - b.exemplar);\n if (repair.length === 0) {\n return null;\n }\n const { repair: _previous, ...first } = request;\n return {\n ...first,\n shape: replyShape(\n repair.map((one) => request.exemplars[one.exemplar] as Fields),\n repair.length,\n ),\n repair,\n };\n}\n\n// ── The loop ───────────────────────────────────────────────────────────\n\nconst RANK = { complete: 2, incomplete: 1, invalid: 0 } as const;\n\nconst invalidCount = (one: AiGeneratedVariant): number =>\n one.mismatches.length +\n one.validation.issues.filter((found) => found.severity === 'invalid').length;\nconst warningCount = (one: AiGeneratedVariant): number =>\n one.findings.filter((found) => found.severity === 'warning').length;\n\n/** Whether a repaired variant is at least as good as the one it would replace. */\nfunction noWorse(repaired: AiGeneratedVariant, before: AiGeneratedVariant): boolean {\n const now = RANK[repaired.status];\n const was = RANK[before.status];\n if (now !== was) {\n return now > was;\n }\n if (invalidCount(repaired) !== invalidCount(before)) {\n return invalidCount(repaired) < invalidCount(before);\n }\n return warningCount(repaired) <= warningCount(before);\n}\n\n/**\n * Writes variants of existing items, with a bounded repair loop: asks the port\n * with `request`, reads the reply as {@link checkAiVariants} does, then — up\n * to `repairs` times — sends back what the model can fix and what it did not\n * write ({@link aiVariantsRepairRequest}), keeping each variant's best pass:\n * by status, then fewer invalid problems, then fewer warnings. A repaired\n * variant keeps its id. With `balanceKeyPositions`, the keys are spread once,\n * over the variants as they end.\n *\n * Never throws for a model's failure: a port that throws or a reply the SDK\n * refuses ends the loop, and is recorded in `calls` beside the provenance and\n * usage of every call. A first call that fails leaves no variants. A request\n * with no exemplar to vary makes no call.\n *\n * ```ts\n * const run = await generateVariants({\n * request: aiVariantsRequest({ exemplars: section, level: 'CEFR B1', avoid: otherForms }),\n * port: (request) => callMyModel(VARIANTS_PROMPT, request),\n * newId: () => crypto.randomUUID(),\n * });\n * // run.variants: store each as proposed, for a person to approve.\n * ```\n *\n * @throws RangeError when `repairs` is not a whole number from 0 to\n * {@link AI_VARIANTS_MAX_REPAIRS} (default 1), and Error when `newId` returns\n * an empty or repeated id.\n */\nexport async function generateVariants(input: {\n request: AiVariantsRequest;\n port: (request: AiVariantsRequest) => Promise<unknown> | unknown;\n newId: () => string;\n repairs?: number;\n}): Promise<AiVariantsRun> {\n const { request, port, newId } = input;\n const repairs = input.repairs ?? 1;\n if (!Number.isInteger(repairs) || repairs < 0 || repairs > AI_VARIANTS_MAX_REPAIRS) {\n throw new RangeError(\n `generateVariants: repairs must be a whole number from 0 to ${AI_VARIANTS_MAX_REPAIRS}, not ${String(repairs)}.`,\n );\n }\n const calls: AiDraftsCall[] = [];\n const skipped = request.skipped;\n if (request.facts.exemplars.length === 0) {\n return { variants: [], missing: [], findings: [], skipped, calls };\n }\n // One source of ids for the whole run, so no id is issued twice across passes.\n const ids = idSource(newId, 'generateVariants');\n\n const ask = async (\n asked: AiVariantsRequest,\n keep: ReadonlyMap<number, string>,\n ): Promise<\n { built: Built[]; variants: AiGeneratedVariant[]; missing: AiVariantMissing[] } | undefined\n > => {\n let raw: unknown;\n try {\n raw = await port(asked);\n } catch (error) {\n calls.push({ ok: false, error: error instanceof Error ? error.message : String(error) });\n return undefined;\n }\n const result = read(raw, asked, ids, keep);\n if (!result.ok) {\n calls.push({ ok: false, refusal: result.refusal });\n return undefined;\n }\n const provenance = provenanceOf(result.raw.provenance);\n const usage = usageOf(result.raw.usage);\n calls.push({\n ok: true,\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n });\n return {\n built: result.built,\n variants: result.built.map((one) => evaluate(one, request)),\n missing: result.missing,\n };\n };\n\n const first = await ask(request, new Map());\n if (first === undefined) {\n return { variants: [], missing: [], findings: [], skipped, calls };\n }\n const best = new Map<number, { built: Built; variant: AiGeneratedVariant }>();\n first.variants.forEach((variant, index) => {\n best.set(variant.exemplarIndex, { built: first.built[index] as Built, variant });\n });\n const wanted = request.facts.exemplars.map((one) => one.exemplar);\n const missingNow = () => wanted.filter((index) => !best.has(index)).map(missingOf);\n\n for (let pass = 0; pass < repairs; pass += 1) {\n const repairRequest = aiVariantsRepairRequest(request, {\n variants: [...best.values()].map((one) => one.variant),\n missing: missingNow(),\n findings: [],\n });\n if (repairRequest === null) {\n break;\n }\n const keep = new Map(\n (repairRequest.repair ?? []).flatMap((one) => {\n const before = best.get(one.exemplar);\n return before !== undefined\n ? [[one.exemplar, stringOf(before.variant.entry.id)] as const]\n : [];\n }),\n );\n const repaired = await ask(repairRequest, keep);\n if (repaired === undefined) {\n break;\n }\n repaired.variants.forEach((variant, index) => {\n const before = best.get(variant.exemplarIndex);\n if (before === undefined || noWorse(variant, before.variant)) {\n best.set(variant.exemplarIndex, { built: repaired.built[index] as Built, variant });\n }\n });\n }\n\n const ended = [...best.values()].sort(\n (a, b) => a.variant.exemplarIndex - b.variant.exemplarIndex,\n );\n const seed = request.balanceKeyPositions?.seed;\n const variants =\n seed !== undefined\n ? balanceKeys(\n ended.map((one) => one.built),\n seed,\n ).map((one) => evaluate(one, request))\n : ended.map((one) => one.variant);\n return { variants, missing: missingNow(), findings: setFindings(variants), skipped, calls };\n}\n","import { aiAllowedByContent, aiSupports, readAiTextResult } from './ai.js';\nimport { materialFacts } from './ai-material.js';\nimport {\n AI_WRITING_MAX_FIELD_LENGTH,\n checkCorrections,\n isRecord,\n MAX_TAG_LENGTH,\n textField,\n} from './ai-written.js';\nimport { countWords } from './count-words.js';\nimport { gradeFromRubric } from './grading.js';\nimport type { LearnerResponse, WrittenResponseData } from './types/activity.js';\nimport type {\n AiActivityInput,\n AiGroupInput,\n AiRefusal,\n AiRubricCriterionFact,\n AiWritingFeedback,\n AiWritingFeedbackRequest,\n} from './types/ai.js';\nimport type { CriterionScore } from './types/grading.js';\n\nexport { AI_WRITING_MAX_CORRECTIONS, AI_WRITING_MAX_FIELD_LENGTH } from './ai-written.js';\n\n/**\n * The request for feedback on a draft, or `null` when there is none to give:\n * not a written response, an author who switched explanations off (writing\n * feedback reads `ai.explanations`), a redacted item — feedback is for\n * practice, where the item is whole — or a draft with nothing in it.\n *\n * The facts are the task as the learner saw it, the draft verbatim with its\n * word count recomputed as the grader will count it, and the rubric the answer\n * will be graded on. `previousFeedback` is the text of the feedback already\n * shown on earlier drafts, so a model can speak to what changed. `group` is the\n * item group the task sits in: its material, the text a summary is of, goes\n * with the request as `material`.\n */\nexport function aiWritingFeedbackRequest(input: {\n data: AiActivityInput;\n response: LearnerResponse | null;\n previousFeedback?: readonly string[];\n group?: AiGroupInput;\n learnerLocale?: string;\n}): AiWritingFeedbackRequest | null {\n const { data, response, learnerLocale } = input;\n if (\n !aiSupports(data.type, 'writing-feedback') ||\n !aiAllowedByContent(data, 'writing-feedback') ||\n (data as { redacted?: unknown }).redacted === true\n ) {\n return null;\n }\n const text = response?.type === 'written-response' ? response.text : '';\n if (typeof text !== 'string' || text.trim() === '') {\n return null;\n }\n const item = data as unknown as WrittenResponseData;\n const wordCount = countWords(text);\n const minWords = typeof item.minWords === 'number' ? item.minWords : 0;\n const maxWords = typeof item.maxWords === 'number' ? item.maxWords : Number.POSITIVE_INFINITY;\n const criteria = Array.isArray(item.rubric?.criteria) ? item.rubric.criteria : null;\n const rubric: AiRubricCriterionFact[] | null =\n criteria === null || criteria.length === 0\n ? null\n : criteria.map((criterion) => ({\n name: criterion.name,\n ...(typeof criterion.description === 'string'\n ? { description: criterion.description }\n : {}),\n weight: criterion.weight,\n }));\n const previousFeedback = [...(input.previousFeedback ?? [])];\n const material = materialFacts(input.group);\n return {\n feature: 'writing-feedback',\n facts: {\n activityType: 'written-response',\n activityId: data.id,\n title: data.title,\n ...(typeof data.locale === 'string' ? { locale: data.locale } : {}),\n prompt: typeof item.prompt === 'string' ? item.prompt : '',\n text,\n wordCount,\n minWords,\n maxWords,\n withinWordBounds: wordCount >= minWords && wordCount <= maxWords,\n rubric,\n ...(typeof item.languageTarget === 'string' ? { languageTarget: item.languageTarget } : {}),\n },\n draftNumber: previousFeedback.length + 1,\n previousFeedback,\n ...(material !== null ? { material } : {}),\n ...(learnerLocale !== undefined ? { learnerLocale } : {}),\n };\n}\n\n/** A criterion score a grade could be computed from: finite, 0 or more, and not above its maximum. */\nfunction isJudgedScore(score: unknown, maxScore: unknown): boolean {\n if (score === undefined) {\n return maxScore === undefined || (typeof maxScore === 'number' && maxScore > 0);\n }\n const max = maxScore ?? 1;\n return (\n typeof score === 'number' &&\n typeof max === 'number' &&\n Number.isFinite(score) &&\n Number.isFinite(max) &&\n max > 0 &&\n score >= 0 &&\n score - max <= max * 1e-9\n );\n}\n\n/** The judgements, each on a criterion the author wrote and weighted as the author weighted it. */\nfunction checkCriteria(\n raw: unknown,\n rubric: readonly AiRubricCriterionFact[] | null,\n): { ok: true; criteria: CriterionScore[] } | { ok: false; refusal: AiRefusal } {\n if (raw === undefined || raw === null) {\n return { ok: true, criteria: [] };\n }\n if (!Array.isArray(raw)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (raw.length === 0) {\n return { ok: true, criteria: [] };\n }\n // Judging a criterion the author did not write is judging a different task.\n if (rubric === null) {\n return { ok: false, refusal: 'malformed' };\n }\n const weights = new Map(rubric.map((criterion) => [criterion.name, criterion.weight]));\n const seen = new Set<string>();\n const criteria: CriterionScore[] = [];\n for (const entry of raw) {\n if (!isRecord(entry) || typeof entry.name !== 'string') {\n return { ok: false, refusal: 'malformed' };\n }\n const weight = weights.get(entry.name);\n if (weight === undefined || seen.has(entry.name)) {\n return { ok: false, refusal: 'malformed' };\n }\n seen.add(entry.name);\n if (!isJudgedScore(entry.score, entry.maxScore)) {\n return { ok: false, refusal: 'malformed' };\n }\n if (entry.notApplicable !== undefined && typeof entry.notApplicable !== 'boolean') {\n return { ok: false, refusal: 'malformed' };\n }\n const band = textField(entry.band, MAX_TAG_LENGTH);\n const comment = textField(entry.comment, AI_WRITING_MAX_FIELD_LENGTH);\n if (!band.ok) {\n return band;\n }\n if (!comment.ok) {\n return comment;\n }\n criteria.push({\n name: entry.name,\n ...(typeof entry.score === 'number' ? { score: entry.score } : {}),\n ...(typeof entry.maxScore === 'number' ? { maxScore: entry.maxScore } : {}),\n ...(band.value !== undefined ? { band: band.value } : {}),\n ...(comment.value !== undefined ? { comment: comment.value } : {}),\n ...(entry.notApplicable === true ? { notApplicable: true } : {}),\n weight,\n });\n }\n return { ok: true, criteria };\n}\n\n/**\n * Checks writing feedback a port returned for `request`, and turns it into what\n * a learner is shown. The SDK shows a model's feedback whole or not at all: it\n * never drops the part it doubts and shows the rest.\n *\n * - **The text** is read as every AI text is (`readAiTextResult`): plain text,\n * not empty, at most {@link AI_TEXT_MAX_LENGTH} code points.\n * - **Every correction must quote the draft** — the check only this SDK can run.\n * The SDK finds each quote in the draft itself, typographic quotes and runs of\n * whitespace aside, and anchors it there; a range the model claims must hold\n * the quote. A correction of words the learner did not write refuses the\n * whole reply as `misquotes-answer`, as a hint that gives the answer away\n * refuses the hint. The `original` shown is the draft's own text at the range.\n * At most {@link AI_WRITING_MAX_CORRECTIONS}; a correction that changes\n * nothing is `malformed`.\n * - **Criteria are judgements; the arithmetic is the SDK's.** Each names a\n * criterion of the item's rubric, once; its score, when it has one, is a\n * score out of its `maxScore`. The rubric's weights are attached, whatever\n * the model sent. Anything else is `malformed`, as is any criterion on an\n * item with no rubric.\n * - **`indicativeScore`** is the rubric's weighted total, computed as\n * `gradeFromRubric` computes a grade, when every criterion of the rubric was\n * judged; `null` otherwise. An indication, never a grade.\n */\nexport function checkAiWritingFeedback(\n raw: unknown,\n request: AiWritingFeedbackRequest,\n): { ok: true; feedback: AiWritingFeedback } | { ok: false; refusal: AiRefusal } {\n const read = readAiTextResult(raw);\n if (!read.ok) {\n return read;\n }\n const record = raw as Record<string, unknown>;\n const corrections = checkCorrections(record.corrections, request.facts.text);\n if (!corrections.ok) {\n return corrections;\n }\n const rubric = request.facts.rubric;\n const criteria = checkCriteria(record.criteria, rubric);\n if (!criteria.ok) {\n return criteria;\n }\n let indicativeScore: number | null = null;\n const judged = new Set(criteria.criteria.map((criterion) => criterion.name));\n if (rubric?.every((criterion) => judged.has(criterion.name))) {\n const grade = gradeFromRubric(criteria.criteria);\n indicativeScore = 'unscorable' in grade ? null : grade.score;\n }\n const { text, provenance, usage } = read.result;\n return {\n ok: true,\n feedback: {\n text,\n corrections: corrections.corrections,\n criteria: criteria.criteria,\n indicativeScore,\n ...(provenance !== undefined ? { provenance } : {}),\n ...(usage !== undefined ? { usage } : {}),\n },\n };\n}\n"],"mappings":"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"}