@intellectif/lk-core 1.4.0 → 1.5.0
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.
- package/CHANGELOG.md +45 -0
- package/README.md +2 -0
- package/dist/{ai-CnmLPMzd.d.cts → ai-BYryFkF9.d.cts} +199 -5
- package/dist/ai-BYryFkF9.d.cts.map +1 -0
- package/dist/{ai-CnmLPMzd.d.ts → ai-BYryFkF9.d.ts} +199 -5
- package/dist/ai-BYryFkF9.d.ts.map +1 -0
- package/dist/ai-check.cjs +175 -17
- package/dist/ai-check.cjs.map +1 -1
- package/dist/ai-check.d.cts +13 -5
- package/dist/ai-check.d.cts.map +1 -1
- package/dist/ai-check.d.ts +13 -5
- package/dist/ai-check.d.ts.map +1 -1
- package/dist/ai-check.js +175 -17
- package/dist/ai-check.js.map +1 -1
- package/dist/{ai-writing-G6tnCGzw.cjs → ai-writing-Cen7-3qs.cjs} +412 -61
- package/dist/ai-writing-Cen7-3qs.cjs.map +1 -0
- package/dist/{ai-writing-CPpH9Qxq.js → ai-writing-D8PdhJJz.js} +395 -62
- package/dist/ai-writing-D8PdhJJz.js.map +1 -0
- package/dist/{index-BAfEHCnD.d.cts → index-Bg0aeWtM.d.cts} +327 -3
- package/dist/index-Bg0aeWtM.d.cts.map +1 -0
- package/dist/{index-2nigSf4N.d.cts → index-CxpiWedW.d.ts} +6 -2
- package/dist/index-CxpiWedW.d.ts.map +1 -0
- package/dist/{index-BqMXWuNC.d.cts → index-D7R3Ll8R.d.cts} +2 -2
- package/dist/{index-BqMXWuNC.d.cts.map → index-D7R3Ll8R.d.cts.map} +1 -1
- package/dist/{index-CVWvymVb.d.ts → index-DUaW1Tj5.d.ts} +327 -3
- package/dist/index-DUaW1Tj5.d.ts.map +1 -0
- package/dist/{index-B54baFUA.d.ts → index-qMbxed1B.d.cts} +6 -2
- package/dist/index-qMbxed1B.d.cts.map +1 -0
- package/dist/{index-CcHDTxIZ.d.ts → index-wMc1E_aM.d.ts} +2 -2
- package/dist/{index-CcHDTxIZ.d.ts.map → index-wMc1E_aM.d.ts.map} +1 -1
- package/dist/index.cjs +31 -130
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -95
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +85 -95
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -117
- package/dist/index.js.map +1 -1
- package/dist/{item-group-DroFXgLb.cjs → item-group-DEB7nLID.cjs} +2 -2
- package/dist/{item-group-DroFXgLb.cjs.map → item-group-DEB7nLID.cjs.map} +1 -1
- package/dist/{item-group-DXiR8nOr.js → item-group-DdSUCUjM.js} +2 -2
- package/dist/{item-group-DXiR8nOr.js.map → item-group-DdSUCUjM.js.map} +1 -1
- package/dist/{registry-DVi0LR_i.cjs → registry-Ce00A_OW.cjs} +184 -4
- package/dist/registry-Ce00A_OW.cjs.map +1 -0
- package/dist/{registry-Dsx9mNsS.js → registry-DaZRpqjW.js} +155 -5
- package/dist/registry-DaZRpqjW.js.map +1 -0
- package/dist/{schemas-Bdw0nGPo.js → schemas-BioKFf3F.js} +3 -3
- package/dist/{schemas-Bdw0nGPo.js.map → schemas-BioKFf3F.js.map} +1 -1
- package/dist/{schemas-DVbAOrtd.cjs → schemas-DksueYsu.cjs} +3 -3
- package/dist/{schemas-DVbAOrtd.cjs.map → schemas-DksueYsu.cjs.map} +1 -1
- package/dist/schemas.cjs +2 -2
- package/dist/schemas.d.cts +1 -1
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.js +2 -2
- package/dist/{scoring-BZqUkuDF.js → scoring-BKNNTz-s.js} +657 -3
- package/dist/scoring-BKNNTz-s.js.map +1 -0
- package/dist/{scoring-B2_HEhMT.cjs → scoring-lKQVYsxy.cjs} +722 -2
- package/dist/scoring-lKQVYsxy.cjs.map +1 -0
- package/dist/scoring.cjs +8 -2
- package/dist/scoring.d.cts +3 -3
- package/dist/scoring.d.ts +3 -3
- package/dist/scoring.js +3 -3
- package/dist/{xapi-DNzkSJ0S.cjs → xapi-NYrTlpID.cjs} +2 -2
- package/dist/{xapi-DNzkSJ0S.cjs.map → xapi-NYrTlpID.cjs.map} +1 -1
- package/dist/{xapi-CMTph7wG.js → xapi-wR6gu4qS.js} +2 -2
- package/dist/{xapi-CMTph7wG.js.map → xapi-wR6gu4qS.js.map} +1 -1
- package/dist/xapi.cjs +1 -1
- package/dist/xapi.d.cts +1 -1
- package/dist/xapi.d.ts +1 -1
- package/dist/xapi.js +1 -2
- package/package.json +2 -2
- package/vectors/README.md +24 -1
- package/vectors/scoring.json +18182 -1
- package/dist/ai-CnmLPMzd.d.cts.map +0 -1
- package/dist/ai-CnmLPMzd.d.ts.map +0 -1
- package/dist/ai-writing-CPpH9Qxq.js.map +0 -1
- package/dist/ai-writing-G6tnCGzw.cjs.map +0 -1
- package/dist/index-2nigSf4N.d.cts.map +0 -1
- package/dist/index-B54baFUA.d.ts.map +0 -1
- package/dist/index-BAfEHCnD.d.cts.map +0 -1
- package/dist/index-CVWvymVb.d.ts.map +0 -1
- package/dist/registry-DVi0LR_i.cjs.map +0 -1
- package/dist/registry-Dsx9mNsS.js.map +0 -1
- package/dist/scoring-B2_HEhMT.cjs.map +0 -1
- package/dist/scoring-BZqUkuDF.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-writing-D8PdhJJz.js","names":["KINDS","isRecord","listOf","isRecord","isRecord","KINDS","isRecord","isRecord","isRecord"],"sources":["../src/ai-material.ts","../src/ai-text.ts","../src/ai.ts","../src/ai-coaching.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-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/**\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 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 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 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 { 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';\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 ...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 ]);\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 five: 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';\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 const critique = descriptor.authoring?.critique;\n if (!isRecord(draft) || critique === undefined) {\n return [];\n }\n return withValidationScope(() => critique(draft)).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 '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 question, the\n * options, the passage, the answers, the hints, the feedback, a picture's\n * `alt`. Settings, ids and addresses are left out, and so is any `…Html`\n * 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('Optional. How to read it, shown to the learner.'),\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', 'question', 'mode', 'options', 'feedback'],\n 'fill-in-the-blanks': ['title', 'passage', 'blanks', 'feedback'],\n 'gap-select': ['title', '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/** 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 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 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 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 const instructions = written(entry.instructions);\n return {\n referenceText: stringOf(entry.referenceText),\n ...(instructions !== undefined ? { instructions } : {}),\n ...feedbackOf(entry),\n };\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 type { LearnerResponse, WrittenResponseData } from './types/activity.js';\nimport type {\n AiActivityInput,\n AiGradedCriterion,\n AiGradingCriterionFact,\n AiGradingJudgement,\n AiGradingRequest,\n AiGradingRequestProblem,\n AiGroupInput,\n AiRefusal,\n} from './types/ai.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\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: AiGradingRequestProblem; 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,\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 { 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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"}
|