@thanh01.pmt/curriculum-kit 1.4.18 → 1.4.20

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/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import { createGoogleGenerativeAI } from '@ai-sdk/google';
4
4
  import { createOpenAI } from '@ai-sdk/openai';
5
5
  import { createDeepSeek } from '@ai-sdk/deepseek';
6
6
  import { z } from 'zod';
7
- import { generateObject, streamObject, streamText } from 'ai';
7
+ import { generateObject, streamObject, streamText, generateText } from 'ai';
8
8
  import pRetry from 'p-retry';
9
9
  import { jsonrepair } from 'jsonrepair';
10
10
  import pLimit from 'p-limit';
@@ -1585,10 +1585,91 @@ var slideProductionWorkflow_exports = {};
1585
1585
  __export(slideProductionWorkflow_exports, {
1586
1586
  GeneratedSlideArraySchema: () => GeneratedSlideArraySchema,
1587
1587
  GeneratedSlideSchema: () => GeneratedSlideSchema,
1588
+ HybridBlueprintArraySchema: () => HybridBlueprintArraySchema,
1589
+ HybridBlueprintItemSchema: () => HybridBlueprintItemSchema,
1590
+ HybridDeckSlideArraySchema: () => HybridDeckSlideArraySchema,
1591
+ HybridDeckSlideSchema: () => HybridDeckSlideSchema,
1592
+ HybridPipelineError: () => HybridPipelineError,
1588
1593
  SlideBlueprintArraySchema: () => SlideBlueprintArraySchema,
1589
1594
  SlideBlueprintItemSchema: () => SlideBlueprintItemSchema,
1590
- executeSlideProductionWorkflow: () => executeSlideProductionWorkflow
1595
+ executeSlideProductionWorkflow: () => executeSlideProductionWorkflow,
1596
+ extractJsonArray: () => extractJsonArray,
1597
+ validateHybridDeckSlides: () => validateHybridDeckSlides
1591
1598
  });
1599
+ function extractJsonArray(raw) {
1600
+ if (!raw) return { error: "empty response" };
1601
+ let text = raw.replace(/<think>[\s\S]*?<\/think>/gi, "").trim();
1602
+ text = text.replace(/^```(?:json)?\s*/m, "").replace(/```\s*$/m, "").trim();
1603
+ const start = text.indexOf("[");
1604
+ const end = text.lastIndexOf("]");
1605
+ if (start < 0 || end <= start) {
1606
+ return {
1607
+ error: "no JSON array span found",
1608
+ head: text.slice(0, 300),
1609
+ tail: text.slice(-300)
1610
+ };
1611
+ }
1612
+ const span = text.slice(start, end + 1);
1613
+ try {
1614
+ return { value: JSON.parse(span) };
1615
+ } catch {
1616
+ }
1617
+ try {
1618
+ return { value: JSON.parse(jsonrepair(span)) };
1619
+ } catch (e) {
1620
+ return {
1621
+ error: "JSON.parse/jsonrepair failed: " + String(e?.message || e).slice(0, 120),
1622
+ head: text.slice(0, 300),
1623
+ tail: text.slice(-300)
1624
+ };
1625
+ }
1626
+ }
1627
+ function validateHybridDeckSlides(slides, blueprint) {
1628
+ const v = [];
1629
+ if (blueprint.length > 0 && slides.length !== blueprint.length) {
1630
+ v.push(`slide count mismatch: got ${slides.length}, blueprint requires ${blueprint.length}`);
1631
+ }
1632
+ slides.forEach((s, i) => {
1633
+ const n = i + 1;
1634
+ const title = typeof s?.title === "string" ? s.title : "";
1635
+ if (!title.trim()) v.push(`slide ${n}: missing/empty title`);
1636
+ else if (title.length > 150) v.push(`slide ${n}: title too long (${title.length} chars, max 150) \u2014 repetition-loop guard`);
1637
+ const notes = typeof s?.notes === "string" ? s.notes.trim() : "";
1638
+ if (notes.length < 80) v.push(`slide ${n}: presenter notes missing or too short (${notes.length} chars, min 80)`);
1639
+ const code = s?.slots?.code;
1640
+ if (typeof code === "string") {
1641
+ if (code.length > 6e3) v.push(`slide ${n}: code block too long (${code.length} chars, max 6000)`);
1642
+ if (/\/\/\s*TODO|<CODE>|your code here/i.test(code)) v.push(`slide ${n}: placeholder code detected (TODO/<CODE>)`);
1643
+ }
1644
+ });
1645
+ return v.slice(0, 10);
1646
+ }
1647
+ async function inferTextJson(schema, systemPrompt, userPrompt, options, label) {
1648
+ const model = getAIModel(options.modelOptions);
1649
+ try {
1650
+ const { text, finishReason } = await generateText({
1651
+ model,
1652
+ system: systemPrompt || void 0,
1653
+ prompt: userPrompt,
1654
+ maxOutputTokens: options.maxOutputTokens ?? 65536
1655
+ });
1656
+ const extracted = extractJsonArray(text);
1657
+ if (!("value" in extracted) || extracted.value === void 0) {
1658
+ console.warn(
1659
+ `[SlideProductionWorkflow] ${label}: JSON extraction failed (${extracted.error}); finish=${finishReason}`,
1660
+ extracted.head ? `head=${String(extracted.head).slice(0, 150)}` : ""
1661
+ );
1662
+ return null;
1663
+ }
1664
+ const validated = schema.safeParse(extracted.value);
1665
+ if (validated.success) return validated.data;
1666
+ console.warn(`[SlideProductionWorkflow] ${label}: output failed schema validation:`, validated.error?.message);
1667
+ return null;
1668
+ } catch (err) {
1669
+ console.warn(`[SlideProductionWorkflow] ${label}: generateText failed:`, err?.message || err);
1670
+ return null;
1671
+ }
1672
+ }
1592
1673
  async function inferStructured(schema, systemPrompt, userPrompt, options, label) {
1593
1674
  try {
1594
1675
  const model = getAIModel(options.modelOptions);
@@ -1630,9 +1711,142 @@ async function inferStructured(schema, systemPrompt, userPrompt, options, label)
1630
1711
  return null;
1631
1712
  }
1632
1713
  }
1714
+ function normalizeBlueprintItems(items) {
1715
+ return items.sort((a, b) => (a.slideIndex ?? 0) - (b.slideIndex ?? 0)).map((item, idx) => ({
1716
+ slideIndex: idx + 1,
1717
+ clusterId: item.clusterId ?? Math.floor(idx / 5) + 1,
1718
+ clusterTitle: item.clusterTitle || "Cluster",
1719
+ lessonPhase: item.lessonPhase || "Content",
1720
+ layoutId: item.layoutId,
1721
+ title: item.title,
1722
+ pedagogicalGoal: item.pedagogicalGoal || "",
1723
+ contentFocus: item.contentFocus || [],
1724
+ codeSnippetIntent: item.codeSnippetIntent ?? void 0,
1725
+ visualIntent: item.visualIntent ?? void 0
1726
+ }));
1727
+ }
1728
+ function buildHybridDeckPrompts(params) {
1729
+ const { lessonFlow, blueprint, skillPrompt, language, languageDirective, headingDirective } = params;
1730
+ const blueprintText = JSON.stringify(
1731
+ blueprint.map((s) => ({
1732
+ slideIndex: s.slideIndex,
1733
+ lessonPhase: s.lessonPhase,
1734
+ layoutId: s.layoutId,
1735
+ title: s.title,
1736
+ pedagogicalGoal: s.pedagogicalGoal,
1737
+ contentFocus: s.contentFocus,
1738
+ codeSnippetIntent: s.codeSnippetIntent ?? void 0,
1739
+ visualIntent: s.visualIntent ?? void 0
1740
+ })),
1741
+ null,
1742
+ 1
1743
+ );
1744
+ const phasesContent = lessonFlow.phases.length > 0 ? lessonFlow.phases.map((p) => `### ${p.phaseName}
1745
+ ${p.content}`).join("\n\n") : lessonFlow.rawContent;
1746
+ const systemPrompt = [
1747
+ skillPrompt,
1748
+ languageDirective,
1749
+ headingDirective,
1750
+ `
1751
+ ### OPERATIONAL GROUND RULES (FULL-DECK AUTHORING):
1752
+ 1. **EXACT COUNT: author EXACTLY ${blueprint.length} slide objects \u2014 ONE per blueprint item, SAME ORDER, SAME layoutId.** Do NOT merge, skip, reorder, or add slides.
1753
+ 2. **ZERO ABBREVIATION / NO "// TODO"**: All code snippets MUST be real, fully authored, compilable code relevant to "${lessonFlow.lessonTitle}". Never write placeholders, stubs, or "<CODE>".
1754
+ 3. **MANDATORY 3-PART PRESENTER NOTES ON EVERY SLIDE (120+ chars)**: SCRIPT (60-90s spoken talk track with an intuitive analogy), COLD CALL / CHECK (one targeted understanding question), SCAFFOLDING / GOTCHA (one common misconception or bug).
1755
+ 4. Titles must be < 100 characters \u2014 never repeat or loop text.
1756
+ 5. Output ONLY the JSON array. No prose, no markdown fences.
1757
+ `.trim()
1758
+ ].filter(Boolean).join("\n\n");
1759
+ const userPrompt = `
1760
+ ### BLUEPRINT (${blueprint.length} slides \u2014 AUTHOR ALL OF THEM, in this exact order):
1761
+ ${blueprintText}
1762
+
1763
+ ### LESSON GROUND TRUTH:
1764
+ - Lesson Title: "${lessonFlow.lessonTitle}"
1765
+ - Target Duration: ${lessonFlow.estimatedDuration}
1766
+ - Pedagogy: "${lessonFlow.pedagogicalModel.toUpperCase()}"
1767
+ - Language: "${language}"
1768
+
1769
+ ### LESSON PHASE CONTENT (SOURCE OF TRUTH FOR REAL CONTENT):
1770
+ ${phasesContent.slice(0, 16e3)}
1771
+
1772
+ ---
1773
+
1774
+ ### OUTPUT:
1775
+ A single JSON array of exactly ${blueprint.length} slide objects:
1776
+ \`\`\`json
1777
+ [
1778
+ {
1779
+ "id": "slide-1",
1780
+ "layoutId": "${blueprint[0]?.layoutId || "split-concept-code"}",
1781
+ "title": "${blueprint[0]?.title || "Slide Title"}",
1782
+ "slots": { "...layout-specific slots with REAL content..." },
1783
+ "notes": "SCRIPT: ...\\nCOLD CALL: ...\\nSCAFFOLDING: ..."
1784
+ }
1785
+ ]
1786
+ \`\`\`
1787
+ `.trim();
1788
+ return { systemPrompt, userPrompt };
1789
+ }
1790
+ async function runHybridPipeline(ctx) {
1791
+ const { lessonFlow, blueprintPrompt, skillPrompt, language, languageDirective, headingDirective, maxRetries, options, onProgress } = ctx;
1792
+ let blueprint = null;
1793
+ for (let attempt = 1; attempt <= maxRetries && !blueprint; attempt++) {
1794
+ onProgress?.("@illustrator", `[1/4] L\u1EADp D\xE0n \xFD Slides \u2014 hybrid blueprint (l\u1EA7n ${attempt}/${maxRetries})...`);
1795
+ const parsed = await inferTextJson(HybridBlueprintArraySchema, "", blueprintPrompt, options, `hybrid-blueprint#${attempt}`);
1796
+ if (parsed && parsed.length > 0) {
1797
+ blueprint = normalizeBlueprintItems(parsed);
1798
+ } else if (attempt < maxRetries) {
1799
+ onProgress?.("@illustrator", `\u26A0\uFE0F Hybrid blueprint l\u1EA7n ${attempt}/${maxRetries} l\u1ED7i \u2014 th\u1EED l\u1EA1i...`, { type: "warning" });
1800
+ }
1801
+ }
1802
+ if (!blueprint || blueprint.length === 0) {
1803
+ throw new HybridPipelineError(`blueprint failed after ${maxRetries} attempts`);
1804
+ }
1805
+ onProgress?.("@illustrator", `[1/4] D\xE0n \xFD ${blueprint.length} slides ho\xE0n t\u1EA5t \u2014 chuy\u1EC3n sang authoring to\xE0n deck...`);
1806
+ const { systemPrompt, userPrompt } = buildHybridDeckPrompts({
1807
+ lessonFlow,
1808
+ blueprint,
1809
+ skillPrompt,
1810
+ language,
1811
+ languageDirective,
1812
+ headingDirective
1813
+ });
1814
+ let slides = null;
1815
+ let lastViolations = [];
1816
+ for (let attempt = 1; attempt <= maxRetries && !slides; attempt++) {
1817
+ onProgress?.("@illustrator", `[2/4] Sinh chi ti\u1EBFt to\xE0n b\u1ED9 ${blueprint.length} slides trong 1 l\u1EA7n g\u1ECDi (l\u1EA7n ${attempt}/${maxRetries})...`);
1818
+ const feedback = attempt > 1 && lastViolations.length > 0 ? `
1819
+
1820
+ ### \u26A0\uFE0F PREVIOUS ATTEMPT REJECTED \u2014 fix these violations:
1821
+ ${lastViolations.map((x) => "- " + x).join("\n")}
1822
+ Return exactly ${blueprint.length} slides, same order as the blueprint.` : "";
1823
+ const parsed = await inferTextJson(HybridDeckSlideArraySchema, systemPrompt, userPrompt + feedback, options, `hybrid-author#${attempt}`);
1824
+ if (!parsed || parsed.length === 0) {
1825
+ lastViolations = ["Output missing, empty, or not a valid JSON array of slide objects"];
1826
+ } else {
1827
+ const violations = validateHybridDeckSlides(parsed, blueprint);
1828
+ if (violations.length === 0) {
1829
+ slides = parsed;
1830
+ break;
1831
+ }
1832
+ lastViolations = violations;
1833
+ }
1834
+ if (attempt < maxRetries) {
1835
+ onProgress?.("@illustrator", `\u26A0\uFE0F Deck authoring l\u1EA7n ${attempt}/${maxRetries} vi ph\u1EA1m guardrails \u2014 retry v\u1EDBi corrective feedback...`, {
1836
+ type: "warning",
1837
+ violations: lastViolations
1838
+ });
1839
+ }
1840
+ }
1841
+ if (!slides) {
1842
+ throw new HybridPipelineError(
1843
+ `deck authoring failed after ${maxRetries} attempts. Last violations: ${lastViolations.slice(0, 3).join("; ")}`
1844
+ );
1845
+ }
1846
+ return { slides, blueprint };
1847
+ }
1633
1848
  async function executeSlideProductionWorkflow(options) {
1634
1849
  const {
1635
- lessonMarkdown,
1636
1850
  lessonCode,
1637
1851
  lessonTitle,
1638
1852
  targetSlideCount,
@@ -1643,6 +1857,7 @@ async function executeSlideProductionWorkflow(options) {
1643
1857
  maxRetries = 3,
1644
1858
  onProgress
1645
1859
  } = options;
1860
+ const engineRequested = options.engine ?? "hybrid";
1646
1861
  let presentationKitSkills = null;
1647
1862
  let presentationKitCore = null;
1648
1863
  try {
@@ -1653,117 +1868,140 @@ async function executeSlideProductionWorkflow(options) {
1653
1868
  presentationKitCore = await import('@thanh01.pmt/presentation-kit');
1654
1869
  } catch {
1655
1870
  }
1656
- const countLabel = targetSlideCount ? `${targetSlideCount} ` : "h\u1EEFu c\u01A1 ";
1657
- onProgress?.("@illustrator", `[1/4] Ph\xE2n t\xEDch m\u1EA1ch b\xE0i gi\u1EA3ng LESSON & L\u1EADp D\xE0n \xFD ${countLabel}Slides...`);
1658
- const lessonFlow = parseLessonFlow(lessonMarkdown);
1871
+ const lessonFlow = parseLessonFlow(options.lessonMarkdown);
1659
1872
  const getStylePreset = presentationKitSkills?.getStylePreset;
1660
1873
  const stylePreset = getStylePreset ? getStylePreset(stylePresetId) : { name: "Blue Professional" };
1874
+ const skillPrompt = presentationKitSkills?.getFrontendSlidesSkillPrompt ? presentationKitSkills.getFrontendSlidesSkillPrompt({ stylePresetId }) : "";
1661
1875
  const blueprintPrompt = buildSlideBlueprintPrompt({
1662
1876
  lessonFlow,
1663
1877
  targetSlideCount,
1664
1878
  stylePresetName: stylePreset?.name || "Blue Professional"
1665
1879
  });
1666
- const blueprintItems = await pRetry(
1667
- async () => {
1668
- const items = await inferStructured(
1669
- SlideBlueprintArraySchema,
1670
- "",
1671
- blueprintPrompt,
1672
- options,
1673
- "blueprint"
1674
- );
1675
- if (!items || items.length === 0) {
1676
- throw new Error("Blueprint generation returned empty or schema-invalid output");
1677
- }
1678
- return items.sort((a, b) => a.slideIndex - b.slideIndex).map((item, idx) => ({
1679
- ...item,
1680
- slideIndex: idx + 1,
1681
- clusterId: item.clusterId || Math.floor(idx / 5) + 1,
1682
- clusterTitle: item.clusterTitle || "Cluster",
1683
- lessonPhase: item.lessonPhase || "Content",
1684
- pedagogicalGoal: item.pedagogicalGoal || "",
1685
- contentFocus: item.contentFocus || []
1686
- }));
1687
- },
1688
- {
1689
- retries: maxRetries - 1,
1690
- onFailedAttempt: (err) => {
1691
- onProgress?.("@illustrator", `\u26A0\uFE0F Blueprint attempt ${err.attemptNumber}/${maxRetries} failed \u2014 retrying...`, {
1692
- type: "warning"
1693
- });
1880
+ let blueprintItems;
1881
+ let allGeneratedSlides;
1882
+ let engineUsed = engineRequested;
1883
+ const hybridResult = engineRequested === "hybrid" ? await runHybridPipeline({
1884
+ lessonFlow,
1885
+ blueprintPrompt,
1886
+ skillPrompt,
1887
+ language,
1888
+ languageDirective,
1889
+ headingDirective,
1890
+ maxRetries,
1891
+ options,
1892
+ onProgress
1893
+ }).catch((hybridErr) => {
1894
+ console.warn(`[SlideProductionWorkflow] Hybrid engine failed: ${hybridErr?.message || hybridErr} \u2014 falling back to chunked pipeline.`);
1895
+ onProgress?.("@illustrator", `\u26A0\uFE0F Hybrid engine l\u1ED7i \u2014 chuy\u1EC3n sang chunked pipeline (per-cluster)...`, { type: "warning" });
1896
+ return null;
1897
+ }) : null;
1898
+ if (hybridResult) {
1899
+ blueprintItems = hybridResult.blueprint;
1900
+ allGeneratedSlides = hybridResult.slides.map((s, idx) => ({
1901
+ id: s.id || `slide-${idx + 1}`,
1902
+ layoutId: s.layoutId,
1903
+ title: s.title,
1904
+ slots: s.slots || {},
1905
+ notes: s.notes || ""
1906
+ }));
1907
+ } else {
1908
+ engineUsed = "chunked";
1909
+ const countLabel = targetSlideCount ? `${targetSlideCount} ` : "h\u1EEFu c\u01A1 ";
1910
+ onProgress?.("@illustrator", `[1/4] Ph\xE2n t\xEDch m\u1EA1ch b\xE0i gi\u1EA3ng LESSON & L\u1EADp D\xE0n \xFD ${countLabel}Slides...`);
1911
+ blueprintItems = await pRetry(
1912
+ async () => {
1913
+ const items = await inferStructured(
1914
+ SlideBlueprintArraySchema,
1915
+ "",
1916
+ blueprintPrompt,
1917
+ options,
1918
+ "blueprint"
1919
+ );
1920
+ if (!items || items.length === 0) {
1921
+ throw new Error("Blueprint generation returned empty or schema-invalid output");
1922
+ }
1923
+ return normalizeBlueprintItems(items);
1924
+ },
1925
+ {
1926
+ retries: maxRetries - 1,
1927
+ onFailedAttempt: (err) => {
1928
+ onProgress?.("@illustrator", `\u26A0\uFE0F Blueprint attempt ${err.attemptNumber}/${maxRetries} failed \u2014 retrying...`, {
1929
+ type: "warning"
1930
+ });
1931
+ }
1694
1932
  }
1695
- }
1696
- );
1697
- const clustersMap = /* @__PURE__ */ new Map();
1698
- for (const item of blueprintItems) {
1699
- const cId = item.clusterId || Math.floor((item.slideIndex - 1) / 5) + 1;
1700
- if (!clustersMap.has(cId)) clustersMap.set(cId, []);
1701
- clustersMap.get(cId).push(item);
1702
- }
1703
- const clusters = Array.from(clustersMap.entries()).sort(([a], [b]) => a - b);
1704
- const skillPrompt = presentationKitSkills?.getFrontendSlidesSkillPrompt ? presentationKitSkills.getFrontendSlidesSkillPrompt({ stylePresetId }) : "";
1705
- const allGeneratedSlides = [];
1706
- const failedClusters = [];
1707
- let clusterIdx = 0;
1708
- for (const [cId, clusterSlides] of clusters) {
1709
- clusterIdx++;
1710
- const clusterTitle = clusterSlides[0]?.clusterTitle || `Giai \u0111o\u1EA1n ${clusterIdx}`;
1711
- onProgress?.("@illustrator", `[2/4] Sinh chi ti\u1EBFt C\u1EE5m ${clusterIdx}/${clusters.length}: "${clusterTitle}" (${clusterSlides.length} slides)...`);
1712
- const clusterPhaseNames = new Set(clusterSlides.map((s) => s.lessonPhase.toLowerCase()));
1713
- const matchingPhases = lessonFlow.phases.filter((p) => clusterPhaseNames.has(p.phaseName.toLowerCase()));
1714
- const lessonExcerpt = matchingPhases.length > 0 ? matchingPhases.map((p) => `### ${p.phaseName}
1933
+ );
1934
+ const clustersMap = /* @__PURE__ */ new Map();
1935
+ for (const item of blueprintItems) {
1936
+ const cId = item.clusterId || Math.floor((item.slideIndex - 1) / 5) + 1;
1937
+ if (!clustersMap.has(cId)) clustersMap.set(cId, []);
1938
+ clustersMap.get(cId).push(item);
1939
+ }
1940
+ const clusters = Array.from(clustersMap.entries()).sort(([a], [b]) => a - b);
1941
+ allGeneratedSlides = [];
1942
+ const failedClusters = [];
1943
+ let clusterIdx = 0;
1944
+ for (const [cId, clusterSlides] of clusters) {
1945
+ clusterIdx++;
1946
+ const clusterTitle = clusterSlides[0]?.clusterTitle || `Giai \u0111o\u1EA1n ${clusterIdx}`;
1947
+ onProgress?.("@illustrator", `[2/4] Sinh chi ti\u1EBFt C\u1EE5m ${clusterIdx}/${clusters.length}: "${clusterTitle}" (${clusterSlides.length} slides)...`);
1948
+ const clusterPhaseNames = new Set(clusterSlides.map((s) => s.lessonPhase.toLowerCase()));
1949
+ const matchingPhases = lessonFlow.phases.filter((p) => clusterPhaseNames.has(p.phaseName.toLowerCase()));
1950
+ const lessonExcerpt = matchingPhases.length > 0 ? matchingPhases.map((p) => `### ${p.phaseName}
1715
1951
  ${p.content}`).join("\n\n") : lessonFlow.rawContent.slice(0, 3500);
1716
- const { systemPrompt, userPrompt } = buildSlideBatchPrompt({
1717
- clusterId: cId,
1718
- clusterTitle,
1719
- clusterSlides,
1720
- lessonFlow,
1721
- lessonExcerpt,
1722
- skillPrompt,
1723
- language,
1724
- languageDirective,
1725
- headingDirective
1726
- });
1727
- try {
1728
- const batchSlides = await pRetry(
1729
- async () => {
1730
- const slides = await inferStructured(
1731
- GeneratedSlideArraySchema,
1732
- systemPrompt,
1733
- userPrompt,
1734
- options,
1735
- `cluster-${cId}`
1736
- );
1737
- if (!slides || slides.length === 0) {
1738
- throw new Error(`Cluster ${cId} returned empty or schema-invalid output`);
1739
- }
1740
- return slides;
1741
- },
1742
- {
1743
- retries: maxRetries - 1,
1744
- onFailedAttempt: (err) => {
1745
- onProgress?.("@illustrator", `\u26A0\uFE0F C\u1EE5m ${clusterIdx} l\u1EA7n ${err.attemptNumber}/${maxRetries} l\u1ED7i \u2014 retry ri\xEAng c\u1EE5m n\xE0y...`, {
1746
- type: "warning"
1747
- });
1952
+ const { systemPrompt, userPrompt } = buildSlideBatchPrompt({
1953
+ clusterId: cId,
1954
+ clusterTitle,
1955
+ clusterSlides,
1956
+ lessonFlow,
1957
+ lessonExcerpt,
1958
+ skillPrompt,
1959
+ language,
1960
+ languageDirective,
1961
+ headingDirective
1962
+ });
1963
+ try {
1964
+ const batchSlides = await pRetry(
1965
+ async () => {
1966
+ const slides = await inferStructured(
1967
+ GeneratedSlideArraySchema,
1968
+ systemPrompt,
1969
+ userPrompt,
1970
+ options,
1971
+ `cluster-${cId}`
1972
+ );
1973
+ if (!slides || slides.length === 0) {
1974
+ throw new Error(`Cluster ${cId} returned empty or schema-invalid output`);
1975
+ }
1976
+ return slides;
1977
+ },
1978
+ {
1979
+ retries: maxRetries - 1,
1980
+ onFailedAttempt: (err) => {
1981
+ onProgress?.("@illustrator", `\u26A0\uFE0F C\u1EE5m ${clusterIdx} l\u1EA7n ${err.attemptNumber}/${maxRetries} l\u1ED7i \u2014 retry ri\xEAng c\u1EE5m n\xE0y...`, {
1982
+ type: "warning"
1983
+ });
1984
+ }
1748
1985
  }
1749
- }
1986
+ );
1987
+ allGeneratedSlides.push(...batchSlides);
1988
+ } catch (clusterErr) {
1989
+ failedClusters.push(cId);
1990
+ console.error(`[SlideProductionWorkflow] Cluster ${cId} permanently failed after ${maxRetries} attempts:`, clusterErr?.message || clusterErr);
1991
+ }
1992
+ }
1993
+ if (failedClusters.length > 0) {
1994
+ throw new Error(
1995
+ `[SlideProductionWorkflow] Failed to generate ${failedClusters.length}/${clusters.length} cluster(s) (clusterId: ${failedClusters.join(", ")}) after ${maxRetries} attempts each. Aborting to prevent placeholder/degraded slide output. Review provider keys/model availability and retry.`
1750
1996
  );
1751
- allGeneratedSlides.push(...batchSlides);
1752
- } catch (clusterErr) {
1753
- failedClusters.push(cId);
1754
- console.error(`[SlideProductionWorkflow] Cluster ${cId} permanently failed after ${maxRetries} attempts:`, clusterErr?.message || clusterErr);
1755
1997
  }
1756
1998
  }
1757
- if (failedClusters.length > 0) {
1758
- throw new Error(
1759
- `[SlideProductionWorkflow] Failed to generate ${failedClusters.length}/${clusters.length} cluster(s) (clusterId: ${failedClusters.join(", ")}) after ${maxRetries} attempts each. Aborting to prevent placeholder/degraded slide output. Review provider keys/model availability and retry.`
1760
- );
1761
- }
1762
1999
  onProgress?.("@illustrator", `[3/4] Chu\u1EA9n h\xF3a b\u1ED1 c\u1EE5c v\xE0 bi\xEAn d\u1ECBch 1920\xD71080 Stage Deck (${allGeneratedSlides.length} slides)...`);
1763
2000
  const normalizer = presentationKitCore?.normalizeSlideSlots;
1764
2001
  const normalizedSlides = allGeneratedSlides.map((s, idx) => {
1765
2002
  const base = normalizer ? normalizer(s) : s;
1766
2003
  if (!base.id) base.id = `slide-${idx + 1}`;
2004
+ if (!base.slots || typeof base.slots !== "object") base.slots = {};
1767
2005
  return base;
1768
2006
  });
1769
2007
  const deckJson = {
@@ -1773,16 +2011,19 @@ ${p.content}`).join("\n\n") : lessonFlow.rawContent.slice(0, 3500);
1773
2011
  slides: normalizedSlides
1774
2012
  };
1775
2013
  let compiledHtml;
1776
- const compiler = presentationKitCore?.compileHtmlDeck;
1777
- if (compiler) {
1778
- try {
1779
- const compiled = compiler(deckJson);
1780
- if (compiled?.html) {
1781
- compiledHtml = compiled.html;
1782
- }
1783
- } catch (compErr) {
1784
- console.warn(`[SlideProductionWorkflow] Failed compiling HTML deck:`, compErr?.message || compErr);
2014
+ try {
2015
+ let compiled = null;
2016
+ if (typeof presentationKitCore?.compileHtmlDeckAsync === "function") {
2017
+ compiled = await presentationKitCore.compileHtmlDeckAsync(deckJson);
2018
+ }
2019
+ if (!compiled?.html && typeof presentationKitCore?.compileHtmlDeck === "function") {
2020
+ compiled = presentationKitCore.compileHtmlDeck(deckJson);
2021
+ }
2022
+ if (compiled?.html) {
2023
+ compiledHtml = compiled.html;
1785
2024
  }
2025
+ } catch (compErr) {
2026
+ console.warn(`[SlideProductionWorkflow] Failed compiling HTML deck:`, compErr?.message || compErr);
1786
2027
  }
1787
2028
  const markdownWrapper = `---
1788
2029
  id: "SLIDE_${lessonCode}"
@@ -1806,10 +2047,11 @@ ${JSON.stringify(deckJson, null, 2)}
1806
2047
  compiledHtml,
1807
2048
  markdownWrapper,
1808
2049
  blueprint: blueprintItems,
1809
- slideCount: normalizedSlides.length
2050
+ slideCount: normalizedSlides.length,
2051
+ engine: engineUsed
1810
2052
  };
1811
2053
  }
1812
- var SlideBlueprintItemSchema, SlideBlueprintArraySchema, GeneratedSlideSchema, GeneratedSlideArraySchema;
2054
+ var LAYOUT_ID_ENUM, SlideBlueprintItemSchema, SlideBlueprintArraySchema, GeneratedSlideSchema, GeneratedSlideArraySchema, HybridBlueprintItemSchema, HybridBlueprintArraySchema, HybridDeckSlideSchema, HybridDeckSlideArraySchema, HybridPipelineError;
1813
2055
  var init_slideProductionWorkflow = __esm({
1814
2056
  "src/services/slideProductionWorkflow.ts"() {
1815
2057
  init_lessonFlowParser();
@@ -1817,37 +2059,61 @@ var init_slideProductionWorkflow = __esm({
1817
2059
  init_slideBatchPrompt();
1818
2060
  init_provider_factory();
1819
2061
  init_streamRunner();
2062
+ LAYOUT_ID_ENUM = z.enum([
2063
+ "hero-cover",
2064
+ "split-concept-code",
2065
+ "two-columns-compare",
2066
+ "three-cards-grid",
2067
+ "timeline-steps",
2068
+ "metric-callout",
2069
+ "checkpoint-quiz",
2070
+ "tiered-practice-3cards",
2071
+ "summary-takeaways"
2072
+ ]);
1820
2073
  SlideBlueprintItemSchema = z.object({
1821
2074
  slideIndex: z.number().int().min(1),
1822
- clusterId: z.number().int().min(1).default(1),
1823
- clusterTitle: z.string().default("Cluster"),
1824
- lessonPhase: z.string().default("Content"),
1825
- layoutId: z.enum([
1826
- "hero-cover",
1827
- "split-concept-code",
1828
- "two-columns-compare",
1829
- "three-cards-grid",
1830
- "timeline-steps",
1831
- "metric-callout",
1832
- "checkpoint-quiz",
1833
- "tiered-practice-3cards",
1834
- "summary-takeaways"
1835
- ]),
2075
+ clusterId: z.number().int().min(1).nullish().default(1),
2076
+ clusterTitle: z.string().nullish().default("Cluster"),
2077
+ lessonPhase: z.string().nullish().default("Content"),
2078
+ layoutId: LAYOUT_ID_ENUM,
1836
2079
  title: z.string().min(1),
1837
- pedagogicalGoal: z.string().default(""),
1838
- contentFocus: z.array(z.string()).default([]),
1839
- codeSnippetIntent: z.string().optional(),
1840
- visualIntent: z.string().optional()
2080
+ pedagogicalGoal: z.string().nullish().default(""),
2081
+ contentFocus: z.array(z.string()).nullish().default([]),
2082
+ codeSnippetIntent: z.string().nullish(),
2083
+ visualIntent: z.string().nullish()
1841
2084
  });
1842
2085
  SlideBlueprintArraySchema = z.array(SlideBlueprintItemSchema);
1843
2086
  GeneratedSlideSchema = z.object({
1844
- id: z.string().optional(),
1845
- layoutId: z.string(),
2087
+ id: z.string().nullish(),
2088
+ layoutId: z.string().min(1),
1846
2089
  title: z.string().min(1),
1847
- slots: z.record(z.any()).default({}),
1848
- notes: z.string().default("")
2090
+ slots: z.record(z.any()).nullish().default({}),
2091
+ notes: z.string().nullish().default("")
1849
2092
  });
1850
2093
  GeneratedSlideArraySchema = z.array(GeneratedSlideSchema);
2094
+ HybridBlueprintItemSchema = z.object({
2095
+ slideIndex: z.number().int().min(1),
2096
+ clusterId: z.number().int().min(1).nullish(),
2097
+ clusterTitle: z.string().nullish(),
2098
+ lessonPhase: z.string().nullish(),
2099
+ layoutId: LAYOUT_ID_ENUM,
2100
+ title: z.string().min(1).max(300),
2101
+ pedagogicalGoal: z.string().nullish().default(""),
2102
+ contentFocus: z.array(z.string()).nullish().default([]),
2103
+ codeSnippetIntent: z.string().nullish(),
2104
+ visualIntent: z.string().nullish()
2105
+ });
2106
+ HybridBlueprintArraySchema = z.array(HybridBlueprintItemSchema);
2107
+ HybridDeckSlideSchema = z.object({
2108
+ id: z.string().nullish(),
2109
+ layoutId: z.string().min(1),
2110
+ title: z.string().min(1).max(300),
2111
+ slots: z.record(z.any()).nullish().default({}),
2112
+ notes: z.string().nullish().default("")
2113
+ });
2114
+ HybridDeckSlideArraySchema = z.array(HybridDeckSlideSchema);
2115
+ HybridPipelineError = class extends Error {
2116
+ };
1851
2117
  }
1852
2118
  });
1853
2119
  var LearningObjectiveRowSchema = z.object({
@@ -10676,6 +10942,306 @@ ${lines.join("\n")}
10676
10942
  - For example: if canonical section is "Computational Thinking Focus", your heading must be "## [REQUIRED] ${headings["Computational Thinking Focus"] || "Computational Thinking Focus"}".`;
10677
10943
  }
10678
10944
 
10945
+ // src/services/curriculumHorizon.ts
10946
+ init_errors();
10947
+ var DETAILED_BRIDGE_WINDOW = 2;
10948
+ var BOUNDARY_PEEK_WINDOW = 2;
10949
+ function parseAllSessions(plan, frameworkMarkdown) {
10950
+ if (plan) {
10951
+ const rawSessions = Array.isArray(plan.sessions) ? plan.sessions : Array.isArray(plan) ? plan : [];
10952
+ if (rawSessions.length > 0) {
10953
+ return rawSessions.map((s, idx) => ({
10954
+ id: s.id || `L${String(idx + 1).padStart(2, "0")}`,
10955
+ order: typeof s.order === "number" ? s.order : idx + 1,
10956
+ title: s.title || "",
10957
+ prose_objective: s.prose_objective || s.objective || "",
10958
+ new_keywords: Array.isArray(s.new_keywords) ? s.new_keywords : Array.isArray(s.keywords) ? s.keywords : [],
10959
+ prerequisite_keywords: Array.isArray(s.prerequisite_keywords) ? s.prerequisite_keywords : [],
10960
+ depth_assignments: Array.isArray(s.depth_assignments) ? s.depth_assignments : []
10961
+ }));
10962
+ }
10963
+ }
10964
+ if (frameworkMarkdown && typeof frameworkMarkdown === "string") {
10965
+ const lines = frameworkMarkdown.split("\n");
10966
+ let headerCols = [];
10967
+ const sessions = [];
10968
+ for (const line of lines) {
10969
+ const trimmed = line.trim();
10970
+ if (!trimmed.startsWith("|")) continue;
10971
+ const cols = trimmed.split("|").slice(1, -1).map((c) => c.trim());
10972
+ const lower = cols.map((c) => c.toLowerCase());
10973
+ if (lower.some((c) => c.includes("lesson code") || c === "m\xE3 b\xE0i" || c.includes("m\xE3 b\xE0i h\u1ECDc"))) {
10974
+ headerCols = cols;
10975
+ continue;
10976
+ }
10977
+ if (/^[-: |]+$/.test(trimmed.slice(1, -1))) continue;
10978
+ if (cols.length < 3) continue;
10979
+ let lessonCode = "";
10980
+ let title = "";
10981
+ let objective = "";
10982
+ let concept = "";
10983
+ let keywordsStr = "";
10984
+ if (headerCols.length > 0) {
10985
+ const col = (name) => {
10986
+ const idx = headerCols.findIndex((h) => h.toLowerCase().includes(name));
10987
+ return idx >= 0 ? cols[idx] || "" : "";
10988
+ };
10989
+ lessonCode = (col("lesson code") || col("m\xE3 b\xE0i") || cols[1] || "").replace(/\*\*/g, "").trim();
10990
+ title = (col("title") || col("t\xEAn") || cols[2] || "").replace(/\*\*/g, "").trim();
10991
+ objective = col("learning objective") || col("objective") || col("m\u1EE5c ti\xEAu") || "";
10992
+ concept = col("key concept") || col("concept") || col("kh\xE1i ni\u1EC7m") || "";
10993
+ keywordsStr = col("keywords") || col("t\u1EEB kh\xF3a") || "";
10994
+ } else {
10995
+ lessonCode = (cols[1] || "").replace(/\*\*/g, "").trim();
10996
+ title = (cols[2] || "").replace(/\*\*/g, "").trim();
10997
+ objective = cols[5] || "";
10998
+ concept = cols[4] || "";
10999
+ }
11000
+ if (lessonCode && /^[A-Za-z0-9_\-]+$/.test(lessonCode)) {
11001
+ const keywords = keywordsStr ? keywordsStr.split(/[,;\n]/).map((k) => k.trim().replace(/^`|`$/g, "")).filter(Boolean) : concept ? concept.split(/[,;\n]/).map((k) => k.trim().replace(/^`|`$/g, "")).filter(Boolean) : [];
11002
+ sessions.push({
11003
+ id: lessonCode,
11004
+ order: sessions.length + 1,
11005
+ title: title || lessonCode,
11006
+ prose_objective: objective,
11007
+ new_keywords: keywords
11008
+ });
11009
+ }
11010
+ }
11011
+ if (sessions.length > 0) {
11012
+ return sessions;
11013
+ }
11014
+ }
11015
+ return [];
11016
+ }
11017
+ async function extractCurriculumHorizon(opts) {
11018
+ const { plan, frameworkMarkdown, targetLessonId, storage, projectId } = opts;
11019
+ const sessions = parseAllSessions(plan, frameworkMarkdown);
11020
+ if (sessions.length === 0) {
11021
+ throw new CurriculumError({
11022
+ errorCode: "ERR_LESSON_NOT_IN_SOT",
11023
+ lessonId: targetLessonId,
11024
+ message: `[CurriculumHorizon] No sessions could be parsed from CURRICULUM_PLAN.json or CURRICULUM_FRAMEWORK.md. Fail fast \u2014 cannot construct curriculum horizon without SOT.`,
11025
+ suggestedAction: "Ensure CURRICULUM_PLAN.json or CURRICULUM_FRAMEWORK.md is generated and contains valid sessions.",
11026
+ retryable: false
11027
+ });
11028
+ }
11029
+ const targetCodePattern = new RegExp("^" + targetLessonId.replace(/_/g, "[_-]") + "$", "i");
11030
+ const targetIndex = sessions.findIndex(
11031
+ (s) => s.id === targetLessonId || targetCodePattern.test(s.id)
11032
+ );
11033
+ if (targetIndex < 0) {
11034
+ throw new CurriculumError({
11035
+ errorCode: "ERR_LESSON_NOT_IN_SOT",
11036
+ lessonId: targetLessonId,
11037
+ message: `[CurriculumHorizon] Lesson "${targetLessonId}" was not found among the ${sessions.length} planned sessions. Fail fast \u2014 refusing to generate unanchored lesson.`,
11038
+ suggestedAction: `Check lesson id against planned sessions: [${sessions.map((s) => s.id).join(", ")}]`,
11039
+ retryable: false
11040
+ });
11041
+ }
11042
+ const currentSession = sessions[targetIndex];
11043
+ const compactEnd = Math.max(0, targetIndex - DETAILED_BRIDGE_WINDOW);
11044
+ const compactSessions = sessions.slice(0, compactEnd);
11045
+ const masteredKeywords = Array.from(
11046
+ new Set(
11047
+ compactSessions.flatMap((s) => s.new_keywords || []).map((k) => k.trim()).filter(Boolean)
11048
+ )
11049
+ );
11050
+ const masteredConcepts = Array.from(
11051
+ new Set(
11052
+ compactSessions.map((s) => s.title || s.prose_objective || "").map((t) => t.trim()).filter(Boolean)
11053
+ )
11054
+ );
11055
+ const bridgeStart = Math.max(0, targetIndex - DETAILED_BRIDGE_WINDOW);
11056
+ const bridgeSessions = sessions.slice(bridgeStart, targetIndex);
11057
+ const detailedBridge = [];
11058
+ for (const session of bridgeSessions) {
11059
+ const bridgeIndex = sessions.indexOf(session) + 1;
11060
+ const bridge = {
11061
+ lessonId: session.id,
11062
+ lessonIndex: bridgeIndex,
11063
+ title: session.title,
11064
+ proseObjective: session.prose_objective,
11065
+ keywords: session.new_keywords || []
11066
+ };
11067
+ if (storage && projectId) {
11068
+ try {
11069
+ const candidates = [
11070
+ `_content/${session.id.replace(/_L\d+$/, "")}/LESSON_${session.id}.md`,
11071
+ `_content/LESSON_${session.id}.md`,
11072
+ `LESSON_${session.id}.md`
11073
+ ];
11074
+ let lessonMd = "";
11075
+ for (const c of candidates) {
11076
+ try {
11077
+ const raw = await storage.readArtifact(projectId, c);
11078
+ if (raw) {
11079
+ lessonMd = raw;
11080
+ break;
11081
+ }
11082
+ } catch {
11083
+ }
11084
+ }
11085
+ if (lessonMd) {
11086
+ const ledger = extractSymbolLedger(lessonMd);
11087
+ if (ledger.primarySymbol || ledger.entryFileName || ledger.keySymbols.length > 0) {
11088
+ bridge.symbolLedger = {
11089
+ primaryStructOrClass: ledger.primarySymbol,
11090
+ mainEntryFile: ledger.entryFileName,
11091
+ keyVariables: ledger.keySymbols
11092
+ };
11093
+ }
11094
+ }
11095
+ } catch {
11096
+ }
11097
+ }
11098
+ detailedBridge.push(bridge);
11099
+ }
11100
+ const peekStart = targetIndex + 1;
11101
+ const peekEnd = Math.min(sessions.length, peekStart + BOUNDARY_PEEK_WINDOW);
11102
+ const boundaryPeek = sessions.slice(peekStart, peekEnd).map((s, idx) => ({
11103
+ lessonId: s.id,
11104
+ lessonIndex: peekStart + idx + 1,
11105
+ title: s.title,
11106
+ keywords: s.new_keywords || []
11107
+ }));
11108
+ return {
11109
+ targetLessonId,
11110
+ targetLessonIndex: targetIndex + 1,
11111
+ totalLessons: sessions.length,
11112
+ targetTitle: currentSession.title,
11113
+ targetKeywords: currentSession.new_keywords || [],
11114
+ compactMasterySet: {
11115
+ masteredKeywords,
11116
+ masteredConcepts
11117
+ },
11118
+ detailedBridge,
11119
+ boundaryPeek
11120
+ };
11121
+ }
11122
+ function renderHorizonPromptBlock(horizon) {
11123
+ const {
11124
+ targetLessonId,
11125
+ targetLessonIndex,
11126
+ totalLessons,
11127
+ targetTitle,
11128
+ targetKeywords,
11129
+ compactMasterySet,
11130
+ detailedBridge,
11131
+ boundaryPeek
11132
+ } = horizon;
11133
+ const lines = [
11134
+ `# \u{1F9ED} CURRICULUM HORIZON & SCAFFOLDING MATRIX (Lesson ${targetLessonIndex}/${totalLessons}: "${targetTitle}" [${targetLessonId}])`
11135
+ ];
11136
+ if (compactMasterySet.masteredKeywords.length > 0 || compactMasterySet.masteredConcepts.length > 0) {
11137
+ const rawKeywords = compactMasterySet.masteredKeywords;
11138
+ const displayedKeywords = rawKeywords.length > 20 ? rawKeywords.slice(-20) : rawKeywords;
11139
+ const vocab = displayedKeywords.map((k) => `\`${k}\``).join(", ");
11140
+ const moreSuffix = rawKeywords.length > 20 ? ` *(+${rawKeywords.length - 20} earlier terms)*` : "";
11141
+ const concepts = compactMasterySet.masteredConcepts.length > 0 ? `
11142
+ - **Prior Concept Foundations:** ${compactMasterySet.masteredConcepts.slice(-3).join("; ")}` : "";
11143
+ lines.push(
11144
+ `
11145
+ ## 1. \u{1F393} MASTERED VOCABULARY (Prior Lessons 1..${Math.max(1, targetLessonIndex - 3)} \u2014 Compact Set):`,
11146
+ `- **Mastered Terms & Syntax:** ${vocab || "(Core fundamentals)"}${moreSuffix}${concepts}`
11147
+ );
11148
+ } else if (targetLessonIndex === 1) {
11149
+ lines.push(
11150
+ `
11151
+ ## 1. \u{1F393} PRIOR KNOWLEDGE FRONTIER:`,
11152
+ `- **Entry Point:** Inaugural lesson (Lesson 1/${totalLessons}). Students have no prior course vocabulary. All concepts must be introduced from baseline.`
11153
+ );
11154
+ }
11155
+ if (detailedBridge.length > 0) {
11156
+ lines.push(`
11157
+ ## 2. \u{1F309} IMMEDIATE PREDECESSOR CONTEXT (Bridge Lessons):`);
11158
+ for (const bridge of detailedBridge) {
11159
+ lines.push(`### Lesson ${bridge.lessonIndex} (${bridge.lessonId}): ${bridge.title}`);
11160
+ if (bridge.proseObjective) {
11161
+ const obj = bridge.proseObjective.length > 120 ? bridge.proseObjective.slice(0, 117) + "..." : bridge.proseObjective;
11162
+ lines.push(`- **Objective:** ${obj}`);
11163
+ }
11164
+ if (bridge.keywords.length > 0) {
11165
+ lines.push(`- **Keywords:** ${bridge.keywords.map((k) => `\`${k}\``).join(", ")}`);
11166
+ }
11167
+ if (bridge.symbolLedger) {
11168
+ const parts = [];
11169
+ if (bridge.symbolLedger.primaryStructOrClass) {
11170
+ parts.push(`Primary Struct: \`${bridge.symbolLedger.primaryStructOrClass}\``);
11171
+ }
11172
+ if (bridge.symbolLedger.mainEntryFile) {
11173
+ parts.push(`Entry File: \`${bridge.symbolLedger.mainEntryFile}\``);
11174
+ }
11175
+ if (bridge.symbolLedger.keyVariables?.length) {
11176
+ parts.push(`Symbols: ${bridge.symbolLedger.keyVariables.map((v) => `\`${v}\``).join(", ")}`);
11177
+ }
11178
+ if (parts.length > 0) {
11179
+ lines.push(`- **Code Symbol Ledger:** ${parts.join(" | ")}`);
11180
+ }
11181
+ }
11182
+ }
11183
+ lines.push(
11184
+ `\u{1F449} INSTRUCTION: Seamlessly connect the opening Hook and Guided Practice by referencing the student's recent work from Lesson ${detailedBridge[detailedBridge.length - 1]?.lessonIndex}.`
11185
+ );
11186
+ }
11187
+ const currentKeywordsStr = targetKeywords.length > 0 ? targetKeywords.map((k) => `\`${k}\``).join(", ") : "`Current Lesson Concepts`";
11188
+ lines.push(
11189
+ `
11190
+ ## 3. \u{1F3AF} ALLOWED DESIGN SPACE (Positive-Only Allow List):`,
11191
+ `- **Student Toolkit:** Mastered Vocabulary + Immediate Bridge Keywords + Current Lesson Scope (${currentKeywordsStr}).`,
11192
+ `- **MANDATE:** ALL extension challenges (EXT), hands-on activities (ACT), and assessment questions (QUIZ) MUST be 100% solvable using ONLY items within this toolkit. Do NOT require unintroduced APIs!`
11193
+ );
11194
+ if (boundaryPeek.length > 0) {
11195
+ const nextLesson = boundaryPeek[0];
11196
+ const nextKeywords = nextLesson.keywords.length > 0 ? nextLesson.keywords.map((k) => `\`${k}\``).join(", ") : "upcoming features";
11197
+ lines.push(
11198
+ `
11199
+ ## 4. \u{1F6A7} BOUNDARY (Next Lesson Peek):`,
11200
+ `- **Upcoming Lesson ${nextLesson.lessonIndex} ("${nextLesson.title}"):** Introduces ${nextKeywords}.`,
11201
+ `- **BOUNDARY MANDATE:** These concepts are NOT yet available to the student. Do not introduce or require these future concepts.`
11202
+ );
11203
+ }
11204
+ return lines.join("\n");
11205
+ }
11206
+ function validateHorizonCompliance(artifactContent, horizon) {
11207
+ const issues = [];
11208
+ const forbiddenMatches = [];
11209
+ if (!artifactContent || horizon.boundaryPeek.length === 0) {
11210
+ return { compliant: true, issues: [], forbiddenMatches: [] };
11211
+ }
11212
+ const contentLower = artifactContent.toLowerCase();
11213
+ const immediateNext = horizon.boundaryPeek[0];
11214
+ if (immediateNext && immediateNext.keywords.length > 0) {
11215
+ for (const kw of immediateNext.keywords) {
11216
+ const trimmed = kw.trim().toLowerCase();
11217
+ if (trimmed.length <= 2) continue;
11218
+ const pattern = new RegExp(`\\b${trimmed.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "i");
11219
+ if (pattern.test(contentLower)) {
11220
+ const inMastered = horizon.compactMasterySet.masteredKeywords.some(
11221
+ (m) => m.toLowerCase() === trimmed
11222
+ );
11223
+ const inBridge = horizon.detailedBridge.some(
11224
+ (b) => b.keywords.some((bk) => bk.toLowerCase() === trimmed)
11225
+ );
11226
+ const inCurrent = horizon.targetKeywords.some(
11227
+ (ck) => ck.toLowerCase() === trimmed
11228
+ );
11229
+ if (!inMastered && !inBridge && !inCurrent) {
11230
+ forbiddenMatches.push(kw);
11231
+ issues.push(
11232
+ `Artifact contains boundary concept "${kw}" scheduled for future Lesson ${immediateNext.lessonIndex} ("${immediateNext.title}").`
11233
+ );
11234
+ }
11235
+ }
11236
+ }
11237
+ }
11238
+ return {
11239
+ compliant: issues.length === 0,
11240
+ issues,
11241
+ forbiddenMatches
11242
+ };
11243
+ }
11244
+
10679
11245
  // src/services/contextBuilder.ts
10680
11246
  var DEFAULT_LESSON_PRIORITIES = [
10681
11247
  "Symbol & Identifier Ledger",
@@ -25490,6 +26056,32 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
25490
26056
 
25491
26057
  [CURRICULUM FRAMEWORK EXCERPT]:
25492
26058
  ${buildFrameworkExcerptForLesson(framework, lessonId)}`;
26059
+ let horizon = null;
26060
+ let horizonBlock = "";
26061
+ try {
26062
+ let planObj = null;
26063
+ const planRaw = await storage.readArtifact(projectId, "_sot/CURRICULUM_PLAN.json");
26064
+ if (planRaw) {
26065
+ try {
26066
+ planObj = JSON.parse(planRaw);
26067
+ } catch {
26068
+ }
26069
+ }
26070
+ horizon = await extractCurriculumHorizon({
26071
+ plan: planObj,
26072
+ frameworkMarkdown: framework,
26073
+ targetLessonId: lessonCode,
26074
+ storage,
26075
+ projectId
26076
+ });
26077
+ if (horizon) {
26078
+ horizonBlock = `
26079
+
26080
+ ${renderHorizonPromptBlock(horizon)}`;
26081
+ }
26082
+ } catch (hErr) {
26083
+ console.warn(`[produceSingleLesson] Notice: Curriculum Horizon extraction:`, hErr?.message || hErr);
26084
+ }
25493
26085
  const glossaryBlock = glossaryContext ? `
25494
26086
 
25495
26087
  [GLOSSARY TERMS (use these exact definitions)]:
@@ -25510,7 +26102,7 @@ ${sessionSliceContext}` : "";
25510
26102
  ${sg}
25511
26103
 
25512
26104
  [REFERENCE PACK GROUND TRUTH]:
25513
- ${ref}${standardsBlock}${glossaryBlock}${expositionBlock}${sessionSliceBlock}`;
26105
+ ${ref}${standardsBlock}${glossaryBlock}${expositionBlock}${sessionSliceBlock}${horizonBlock}`;
25514
26106
  let commonContext = assembleCommonContext(effectiveRefPack, effectiveStyleGuide);
25515
26107
  const MAX_COMPOSITE_PROMPT_CHARS = 3e4;
25516
26108
  const runnerOptions = options.customInference ? { customInference: options.customInference } : {};
@@ -26405,14 +26997,12 @@ ${buildHeadingDirective("CODE", slcMarkdown, targetLang)}`;
26405
26997
  c3: "### Challenge 3: [Name] (Difficulty: \u2B50\u2B50\u2B50\u2B50\u2B50) \u2014 Extension Milestones (Level 1: Ninja \u2192 Level 2: Guru \u2192 Level 3: Master)",
26406
26998
  meta: "## [REQUIRED] Metacognitive Deep Dive & Engineering Trade-offs"
26407
26999
  };
26408
- const lessonIdxMatch = lessonCode.match(/L0*(\d+)/i);
26409
- const lessonIdx = lessonIdxMatch ? parseInt(lessonIdxMatch[1], 10) : 1;
26410
- const isEarlyLesson = lessonIdx <= 4;
26411
- const zpdCeilingPrompt = isEarlyLesson ? `
26412
- 4. CRITICAL ZPD SCOPE LOCK (LESSON #${lessonIdx} - FOUNDATIONAL):
26413
- - This is an early foundational lesson. The extension challenge MUST focus on creative design variations, parameterization, or edge cases of the current lesson concepts ONLY.
26414
- - STRICTLY FORBIDDEN: NEVER introduce advanced mechanisms from future lessons (No complex event buses, No remote network/API calls, No persistent databases, No async concurrency, No out-of-scope hardware/sensors).` : `
26415
- 4. ZPD SCOPE: Focus on architectural edge cases, algorithmic optimization, and synthesis of learned concepts up to Lesson #${lessonIdx}.`;
27000
+ const zpdCeilingPrompt = horizon ? `
27001
+ 4. \u{1F3AF} CURRICULUM HORIZON ALLOWED SPACE & BOUNDARY MANDATE:
27002
+ - Student's COMPLETE allowed toolkit = Mastered Vocabulary + Immediate Bridge + Current Lesson Scope (${horizon.targetKeywords.join(", ") || "Current Lesson Concepts"}).
27003
+ - All extension challenges MUST be 100% solvable using ONLY items within this toolkit.
27004
+ ${horizon.boundaryPeek.length > 0 ? `- STRICT BOUNDARY: Upcoming Lesson ${horizon.boundaryPeek[0].lessonIndex} introduces "${horizon.boundaryPeek[0].title}" (${horizon.boundaryPeek[0].keywords.join(", ")}). These are STRICTLY OUT OF SCOPE. Do NOT introduce or require these future APIs.` : ""}` : `
27005
+ 4. ZPD SCOPE: Focus on architectural edge cases, algorithmic optimization, and creative variations within current and past lesson concepts.`;
26416
27006
  const extPrompt = `You are @activity (Lead STEM Specialist & Differentiated Extension Designer).
26417
27007
  Based on Master Lesson Plan \`LESSON_${lessonCode}.md\`, author the advanced extension challenge: \`EXT_${lessonCode}.md\`.
26418
27008
 
@@ -26984,6 +27574,20 @@ async function generateSingleArtifact(req) {
26984
27574
  - Entry Source File: \`${symbolLedger.entryFileName || symbolLedger.primarySymbol + ".swift"}\`
26985
27575
  - Key Identifiers to inherit: ${symbolLedger.keySymbols.map((s) => `\`${s}\``).join(", ")}
26986
27576
  - INVARIANT: You MUST use these exact identifier names in all code examples, exercises, and starter templates. Do NOT invent conflicting struct or class names.` : "";
27577
+ let horizon = contextSot.horizon || null;
27578
+ if (!horizon && (contextSot.plan || contextSot.framework) && lessonId) {
27579
+ try {
27580
+ horizon = await extractCurriculumHorizon({
27581
+ plan: contextSot.plan,
27582
+ frameworkMarkdown: contextSot.framework,
27583
+ targetLessonId: lessonId
27584
+ });
27585
+ } catch {
27586
+ }
27587
+ }
27588
+ const horizonPrompt = horizon ? `
27589
+
27590
+ ${renderHorizonPromptBlock(horizon)}` : "";
26987
27591
  const systemPrompt = `You are ${meta.persona}, an expert Curriculum & Pedagogical Specialist in the Curriculum OS Multi-Agent Team.
26988
27592
  Your task is to author a canonical, publication-grade learning artifact of type "${artifactType.toUpperCase()}".
26989
27593
 
@@ -26997,7 +27601,7 @@ ${headingDirective}
26997
27601
  6. DOMAIN & TECH STACK GUARDRAILS:
26998
27602
  ${domainGuardrail}
26999
27603
  ${symbolLedgerPrompt}
27000
- 8. OUTPUT INTEGRITY: Output clean Markdown starting directly with YAML frontmatter. Do not wrap entire response in triple backticks.${artifactSpecializedPrompt}${mediaPolicy ? `
27604
+ 8. OUTPUT INTEGRITY: Output clean Markdown starting directly with YAML frontmatter. Do not wrap entire response in triple backticks.${artifactSpecializedPrompt}${horizonPrompt}${mediaPolicy ? `
27001
27605
 
27002
27606
  IMAGE PLANNING (media ledger) \u2014 QUOTA GEN: t\u1ED1i \u0111a ${mediaPolicy.maxGenImagesPerArtifact} \u1EA3nh AI cho artifact n\xE0y (\u1EA3nh search kho kh\xF4ng gi\u1EDBi h\u1EA1n).
27003
27607
  Khi n\u1ED9i dung c\u1EA7n minh h\u1ECDa (diagram quy tr\xECnh, s\u01A1 \u0111\u1ED3 kh\xE1i ni\u1EC7m, step-by-step visual), ch\xE8n placeholder \u0111\xFAng format [IMAGE: slug] (slug ch\u1EEF th\u01B0\u1EDDng-g\u1EA1ch ngang, unique trong b\xE0i, vd img-for-loop-diagram) T\u1EA0I \u0110\xDANG CH\u1ED6 c\u1EA7n \u1EA3nh, tr\xEAn d\xF2ng ri\xEAng. KH\xD4NG t\u1EF1 sinh \u1EA3nh, KH\xD4NG d\xF9ng markdown image \u2014 placeholder s\u1EBD \u0111\u01B0\u1EE3c thay b\u1EB1ng \u1EA3nh th\u1EADt sau khi Media Curator duy\u1EC7t. Ch\u1EC9 \u0111\u1EB7t cho \u1EA3nh TH\u1EF0C S\u1EF0 c\u1EA7n thi\u1EBFt.` : ""}`;
@@ -27478,13 +28082,9 @@ ${includeUnitTests ? "- Unit Testing: Provide automated assertions or test runne
27478
28082
  - Exact quantities calculated for ${studentCount} students.
27479
28083
  - Component specifications, estimated unit cost, and affordable alternatives.`;
27480
28084
  case "ext": {
27481
- const lessonIdxMatch = (req.lessonId || "").match(/L0*(\d+)/i);
27482
- const lessonIdx = lessonIdxMatch ? parseInt(lessonIdxMatch[1], 10) : 1;
27483
- const isEarlyLesson = lessonIdx <= 4;
27484
- const zpdCeilingRule = isEarlyLesson ? `- \u{1F6D1} ZPD COMPLEXITY CEILING (FOUNDATIONAL LESSON #${lessonIdx}):
27485
- \u2022 SCOPE LOCK: This is an early foundational lesson. The extension challenge MUST focus on creative design variations, parameterization, or edge cases of the current lesson concepts ONLY.
27486
- \u2022 STRICTLY FORBIDDEN: NEVER introduce advanced mechanisms from future lessons (No complex event buses, No remote network/API calls, No persistent databases, No async concurrency, No out-of-scope hardware/sensors).` : `- \u{1F680} ZPD ADVANCED CHALLENGE (LESSON #${lessonIdx}):
27487
- \u2022 Focus on architectural edge cases, algorithmic optimization, and synthesis of learned concepts up to Lesson #${lessonIdx}.`;
28085
+ const zpdCeilingRule = `- \u{1F3AF} CURRICULUM HORIZON ALLOWED SPACE & BOUNDARY MANDATE:
28086
+ \u2022 SCOPE LOCK: All extension challenges MUST be 100% solvable using ONLY items from the student's Mastered Vocabulary and Current Lesson Scope.
28087
+ \u2022 STRICTLY FORBIDDEN: NEVER introduce unintroduced APIs or mechanisms from upcoming lessons (No complex event buses, No remote network/API calls, No persistent databases, No async concurrency, No out-of-scope hardware/sensors unless explicitly part of the allowed toolkit).`;
27488
28088
  return `
27489
28089
  ### \u{1F680} EXTENSION CHALLENGE ARCHITECTURE INVARIANTS:
27490
28090
  ${zpdCeilingRule}
@@ -31115,6 +31715,6 @@ function renderMediaPlaceholder(entry) {
31115
31715
  return `> \u{1F5BC}\uFE0F [\u1EA2nh \u0111ang ch\u1EDD: ${entry.name} \u2014 s\u1EBD \u0111\u01B0\u1EE3c t\u1EA1o sau khi duy\u1EC7t media]`;
31116
31716
  }
31117
31717
 
31118
- export { ACT_TEMPLATE, ARTIFACT_DEPENDENCY_ROUTING, ARTIFACT_EXECUTION_ORDER, AcademicAuditor, ActiveLearningWorkflowSchema, ActivityLabSchema, ActivitySeqRowSchema, ActivityStepSchema, ArtifactContractRowSchema, AssessmentMapRowSchema, AssessmentObjectiveSchema, BELL_RINGER_TEMPLATE, BLOOM_ACTION_VERBS, BOM_TEMPLATE, BUNDLED_STANDARDS_PACK_IDS, BellRingerDaySchema, BellRingerSchema, BloomHintSchema, BloomLevelSchema, BloomTaxonomyEvaluator, BronzeTierSchema, CERConclusionSchema, CHOICE_BOARD_TEMPLATE, CODE_LAB_TEMPLATE, CURRICULUM_SLASH_COMMANDS, CardTierEnum, ChoiceBoardRubricRowSchema, ChoiceBoardSchema, ChoiceRuleSchema, ChoiceSquareSchema, ClassificationSchema, CodeHardwareFeasibilityEvaluator, CodeLabSchema, CodeSnippetSchema, CompetencyRubricRowSchema, ComputationalThinkingSchema, ConstructiveAlignmentEvaluator, CoreConceptSchema, CourseObjectiveSchema, CoverageReportSchema, CrossArtifactDriftEvaluator, CurriculumArtifactSchema, CurriculumError, CurriculumPlanSchema, CurriculumQualityReportSchema, DEFAULT_ARTIFACT_STREAM_IDLE_MS, DEFAULT_ARTIFACT_STREAM_TOTAL_MS, DEFAULT_ENABLED_PROVIDERS, DEFAULT_GATE_SETTINGS, DEFAULT_KX_PRIORITIES, DEFAULT_LESSON_PRIORITIES, DEFAULT_STREAM_IDLE_MS, DEFAULT_STREAM_TOTAL_MS, DEFAULT_VIETNAMESE_SECTION_HEADINGS, DependencyEdgeSchema, DepthAssignmentSchema, DepthLevelSchema, DeterministicPipelineRunner, DeterministicStructuralLinter, DiagnosticQuestionSchema, DiagnosticQuizSchema, EDPPhaseEnum, EXIT_TICKET_TEMPLATE, EXPOSITION_REL, EXT_TEMPLATE, EntryLevelSchema, ExitTicketBugHuntSchema, ExitTicketMetacognitionSchema, ExitTicketQuickItemSchema, ExitTicketRecallPromptSchema, ExitTicketSchema, ExperimentalDesignSchema, ExpositionApprovalError, ExtensionChallengeSchema, ExtensionSchema, FileSystemCurriculumAdapter, FiveEFlowPhaseSchema, FiveEInstructionalEvaluator, FiveEPhaseEnum, FrameworkMappingSchema, FrameworkPackManifestSchema, FrameworkPackSchema, FrameworkStatementSchema, GLOSSARY_TEMPLATE, GRAPHIC_ORGANIZER_TEMPLATE, GeneratedSlideArraySchema, GeneratedSlideSchema, GlossaryQuickRefRowSchema, GlossarySchema, GlossaryTermSchema, GoldTierSchema, GradeBandSchema, GraphicOrganizerItemSchema, GraphicOrganizerSchema, HANDOUT_TEMPLATE, HandoutSchema, HandoutSectionSchema, InstructionSectionSchema, InstructionStepSchema, JudgeCriterionSchema, LAYER_IDLE_BUDGET_MS, LAYER_TOTAL_BUDGET_MS, LESSON_PLAN_TEMPLATE, LLMJudgeEngine, LabTierTaskSchema, LearningObjectiveInputSchema, LearningObjectiveRowSchema, LessonFlowPhaseSchema, LessonPlan5ESchema, LessonPlanEDPSchema, LessonPlanSchema, LessonSectionSchema, LocalWorkspaceManager, MappingKindSchema, MarpSlideSchema, MediaError, MediaLedger, MentorCheatsheetSchema, MilestoneInputSchema, MisconceptionEvaluator, MisconceptionSchema, ModelPolicyResolver, NVIDIA_MODELS, NodeKindSchema, OrganizerTypeEnum, PROJECT_INSTRUCTION_TEMPLATE, PhaseInputSchema, PlanConstraintsSchema, PlannerInputSchema, PlanningGraphSchema, PlanningNodeSchema, PrerequisiteDecisionEntrySchema, PrerequisiteDecisionSchema, ProductGoalSchema, ProgressiveHintsSchema, ProjectGraphSchema, ProjectInstructionSchema, ProjectProfileSchema, QUIZ_TEMPLATE, QualityAuditVerdictSchema, QuizOptionSchema, REVIEW_GAME_TEMPLATE, RUBRIC_TEMPLATE, RecordProvenanceSchema, ResourceMapRowSchema, ReviewGameExportRowSchema, ReviewGameQuestionSchema, ReviewGameSchema, RoadmapInputSchema, RotationStationSchema, RubricCriteriaSchema, RubricSchema, SCIENCE_LAB_TEMPLATE, SELF_LAB_TEMPLATE, SLIDE_CANONICAL_METHODOLOGY, SLIDE_LAYOUT_PRESETS, SLIDE_TEMPLATE, STANDARD_REF_REGEX, STANDARD_SOT_FILES, STATION_ROTATION_TEMPLATE, ScaffoldDecisionEntrySchema, ScaffoldDecisionSchema, ScienceLabSchema, ScoringRubricRowSchema, SelfLabSchema, SelfPacedBundleSchema, SelfPacedChallengeSchema, SessionPlanSchema, SilverTierSchema, SlideBlueprintArraySchema, SlideBlueprintItemSchema, SlideDeckSchema, StandardizedTermRowSchema, StandardsRegistryAdapter, StationRecordRowSchema, StationRotationSchema, StationTypeEnum, SupabaseCurriculumAdapter, TASK_CARDS_TEMPLATE, TEACHER_GUIDE_TEMPLATE, TIERED_PRACTICE_TEMPLATE, TaskCardSchema, TaskCardsSchema, TeacherGuideSchema, TeachingScriptPhaseSchema, TierLevelEnum, TierObjectiveSchema, TieredPracticeSchema, TranslationPolicySchema, TroubleshootingEntrySchema, TroubleshootingRowSchema, UnitPlanSchema, UserJourneySchema, WORKSHEET_TEMPLATE, WorksheetItemSchema, WorksheetItemTypeEnum, WorksheetPartSchema, WorksheetSchema, activityTools, analystTools, analyzeProjectCreationIntent, assertAcyclic, assessorTools, auditCurriculumQualityFlow, auditQualityReport, buildActivityPrompt, buildCodeLabPrompt, buildCurriculumContext, buildCurriculumPlan, buildDeliveryPackages, buildDiagnosticQuizPrompt, buildDomainLexiconGuardrail, buildExpositionContext, buildExpositionExcerpt, buildExtensionPrompt, buildHandoutPrompt, buildHeadingDirective, buildHtmlDeckSlidePrompt, buildImagePrompt, buildJudgePrompt, buildLanguageDirective, buildLessonExcerpt, buildLessonMasterPrompt, buildMarpMarkdownSlidePrompt, buildProjectInstructionPrompt, buildSectionAwareExcerpt, buildSelfLabPrompt, buildSessionSliceContext, buildSlidesPrompt, buildStandardStackMarkdown, buildStandardsContext, buildStandardsContextBlock, buildTeacherGuidePrompt, buildWorksheetPrompt, closeTruncatedJson, computeContentHash, computePackingSpec, conductPreliminaryResearch, contentTools, convertRoadmapToFoundationSot, createAiInferenceError, createContextMissingError, createCurriculumStorage, createIdleAbortController, createStreamAbortController, createStreamAbortSignal, createStreamChunkExtractor, curateMediaLedger, designerTools, detectProjectPedagogy, ensureExpositionForLesson, ensureKnowledgeExposition, evaluateStandardsCoverage, executeCurriculumCommand, executeSlideProductionWorkflow, exportAllProjectQuizzes, expositionCacheKey, extractScopeSequenceRows, extractSectionHeadingsFromSLC, extractSessionSlice, extractStandardRefs, extractStreamChunk, extractSymbolLedger, extractThoughtAndContent, findStandardStatement, formatQuizzesToCsv, fulfillMediaLedger, gateModeFor, generateActivityFlow, generateCodeLabFlow, generateDiagnosticQuizFlow, generateEducationalImage, generateExtensionFlow, generateHandoutFlow, generateLessonMasterFlow, generateMilestoneCurriculumBundle, generatePhase1SotArtifacts, generatePhase2SotArtifacts, generateProjectInstruction, generateProjectInstructionFlow, generateRoadmapCurriculum, generateSelfLabFlow, generateSelfPacedBundle, generateSingleArtifact, generateSlidesFlow, generateTeacherGuideFlow, generateWorksheetFlow, getAIModel, getArtifactMetadata, getDesignatedFallbackChain, getDesignatedFallbackConfig, getLessonMetadata, getProjectArtifactScope, getProjectStatusReport, getSlideLayoutPresetById, getSlideLayoutPresetsByCategory, getSmartResumeContext, glossaryTermsForSession, illustratorTools, importRoadmapToProject, ingestProjectGraphIntoProject, initializeProjectInStorage, injectMediaIntoMarkdown, isExpositionFresh, isModelAllowed, isProviderEnabled, isTranslationDue, lintAndSanitizeArtifact, lintCurriculumFramework, lintFrameworkPack, loadCurriculumTemplate, loadSotTemplate, normalizePlanningGraph, normalizeSlcContract, packagerTools, parseGateSettings, parseQuizMarkdown, parseRoadmapJsonToProjectPayload, produceBatchLessons, produceSingleLesson, publishToGitHub, publishToSupabase, rankGenCandidates, renderFrameworkFromPlan, renderMediaPlaceholder, researcherTools, resolveGateSettings, resolveStandardsPacks, resolveStreamBudget, resolveTargetLanguageCode, resolveTranslationTargets, reviewerTools, runCurriculumAIInference, safeParseJson, searchEducationalImages, searchEducationalVideos, selectStatementsForLesson, serializeActivityToMarkdown, serializeCodeLabToMarkdown, serializeDiagnosticQuizToMarkdown, serializeHandoutToMarkdown, serializeLessonToMarkdown, serializeProjectInstructionToMarkdown, serializeSelfLabToMarkdown, slugifyProjectName, streamCurriculumAIInference, streamCurriculumAIInferenceWithTools, streamDiagnosticQuizFlow, streamLLMWithFallback, streamLayerPrefillWithFallback, streamLessonMasterFlow, streamSelfLabFlow, stripLlmJsonWrappers, targetLanguageDisplayName, techSmeTools, topoSort, uploadAssetToBucket, validateArtifactDependencies, validateCurriculumPlan, validateFrameworkPack, validateMarkdownTables, validateMermaidSyntax, withAutoRepair };
31718
+ export { ACT_TEMPLATE, ARTIFACT_DEPENDENCY_ROUTING, ARTIFACT_EXECUTION_ORDER, AcademicAuditor, ActiveLearningWorkflowSchema, ActivityLabSchema, ActivitySeqRowSchema, ActivityStepSchema, ArtifactContractRowSchema, AssessmentMapRowSchema, AssessmentObjectiveSchema, BELL_RINGER_TEMPLATE, BLOOM_ACTION_VERBS, BOM_TEMPLATE, BUNDLED_STANDARDS_PACK_IDS, BellRingerDaySchema, BellRingerSchema, BloomHintSchema, BloomLevelSchema, BloomTaxonomyEvaluator, BronzeTierSchema, CERConclusionSchema, CHOICE_BOARD_TEMPLATE, CODE_LAB_TEMPLATE, CURRICULUM_SLASH_COMMANDS, CardTierEnum, ChoiceBoardRubricRowSchema, ChoiceBoardSchema, ChoiceRuleSchema, ChoiceSquareSchema, ClassificationSchema, CodeHardwareFeasibilityEvaluator, CodeLabSchema, CodeSnippetSchema, CompetencyRubricRowSchema, ComputationalThinkingSchema, ConstructiveAlignmentEvaluator, CoreConceptSchema, CourseObjectiveSchema, CoverageReportSchema, CrossArtifactDriftEvaluator, CurriculumArtifactSchema, CurriculumError, CurriculumPlanSchema, CurriculumQualityReportSchema, DEFAULT_ARTIFACT_STREAM_IDLE_MS, DEFAULT_ARTIFACT_STREAM_TOTAL_MS, DEFAULT_ENABLED_PROVIDERS, DEFAULT_GATE_SETTINGS, DEFAULT_KX_PRIORITIES, DEFAULT_LESSON_PRIORITIES, DEFAULT_STREAM_IDLE_MS, DEFAULT_STREAM_TOTAL_MS, DEFAULT_VIETNAMESE_SECTION_HEADINGS, DependencyEdgeSchema, DepthAssignmentSchema, DepthLevelSchema, DeterministicPipelineRunner, DeterministicStructuralLinter, DiagnosticQuestionSchema, DiagnosticQuizSchema, EDPPhaseEnum, EXIT_TICKET_TEMPLATE, EXPOSITION_REL, EXT_TEMPLATE, EntryLevelSchema, ExitTicketBugHuntSchema, ExitTicketMetacognitionSchema, ExitTicketQuickItemSchema, ExitTicketRecallPromptSchema, ExitTicketSchema, ExperimentalDesignSchema, ExpositionApprovalError, ExtensionChallengeSchema, ExtensionSchema, FileSystemCurriculumAdapter, FiveEFlowPhaseSchema, FiveEInstructionalEvaluator, FiveEPhaseEnum, FrameworkMappingSchema, FrameworkPackManifestSchema, FrameworkPackSchema, FrameworkStatementSchema, GLOSSARY_TEMPLATE, GRAPHIC_ORGANIZER_TEMPLATE, GeneratedSlideArraySchema, GeneratedSlideSchema, GlossaryQuickRefRowSchema, GlossarySchema, GlossaryTermSchema, GoldTierSchema, GradeBandSchema, GraphicOrganizerItemSchema, GraphicOrganizerSchema, HANDOUT_TEMPLATE, HandoutSchema, HandoutSectionSchema, HybridBlueprintArraySchema, HybridBlueprintItemSchema, HybridDeckSlideArraySchema, HybridDeckSlideSchema, HybridPipelineError, InstructionSectionSchema, InstructionStepSchema, JudgeCriterionSchema, LAYER_IDLE_BUDGET_MS, LAYER_TOTAL_BUDGET_MS, LESSON_PLAN_TEMPLATE, LLMJudgeEngine, LabTierTaskSchema, LearningObjectiveInputSchema, LearningObjectiveRowSchema, LessonFlowPhaseSchema, LessonPlan5ESchema, LessonPlanEDPSchema, LessonPlanSchema, LessonSectionSchema, LocalWorkspaceManager, MappingKindSchema, MarpSlideSchema, MediaError, MediaLedger, MentorCheatsheetSchema, MilestoneInputSchema, MisconceptionEvaluator, MisconceptionSchema, ModelPolicyResolver, NVIDIA_MODELS, NodeKindSchema, OrganizerTypeEnum, PROJECT_INSTRUCTION_TEMPLATE, PhaseInputSchema, PlanConstraintsSchema, PlannerInputSchema, PlanningGraphSchema, PlanningNodeSchema, PrerequisiteDecisionEntrySchema, PrerequisiteDecisionSchema, ProductGoalSchema, ProgressiveHintsSchema, ProjectGraphSchema, ProjectInstructionSchema, ProjectProfileSchema, QUIZ_TEMPLATE, QualityAuditVerdictSchema, QuizOptionSchema, REVIEW_GAME_TEMPLATE, RUBRIC_TEMPLATE, RecordProvenanceSchema, ResourceMapRowSchema, ReviewGameExportRowSchema, ReviewGameQuestionSchema, ReviewGameSchema, RoadmapInputSchema, RotationStationSchema, RubricCriteriaSchema, RubricSchema, SCIENCE_LAB_TEMPLATE, SELF_LAB_TEMPLATE, SLIDE_CANONICAL_METHODOLOGY, SLIDE_LAYOUT_PRESETS, SLIDE_TEMPLATE, STANDARD_REF_REGEX, STANDARD_SOT_FILES, STATION_ROTATION_TEMPLATE, ScaffoldDecisionEntrySchema, ScaffoldDecisionSchema, ScienceLabSchema, ScoringRubricRowSchema, SelfLabSchema, SelfPacedBundleSchema, SelfPacedChallengeSchema, SessionPlanSchema, SilverTierSchema, SlideBlueprintArraySchema, SlideBlueprintItemSchema, SlideDeckSchema, StandardizedTermRowSchema, StandardsRegistryAdapter, StationRecordRowSchema, StationRotationSchema, StationTypeEnum, SupabaseCurriculumAdapter, TASK_CARDS_TEMPLATE, TEACHER_GUIDE_TEMPLATE, TIERED_PRACTICE_TEMPLATE, TaskCardSchema, TaskCardsSchema, TeacherGuideSchema, TeachingScriptPhaseSchema, TierLevelEnum, TierObjectiveSchema, TieredPracticeSchema, TranslationPolicySchema, TroubleshootingEntrySchema, TroubleshootingRowSchema, UnitPlanSchema, UserJourneySchema, WORKSHEET_TEMPLATE, WorksheetItemSchema, WorksheetItemTypeEnum, WorksheetPartSchema, WorksheetSchema, activityTools, analystTools, analyzeProjectCreationIntent, assertAcyclic, assessorTools, auditCurriculumQualityFlow, auditQualityReport, buildActivityPrompt, buildCodeLabPrompt, buildCurriculumContext, buildCurriculumPlan, buildDeliveryPackages, buildDiagnosticQuizPrompt, buildDomainLexiconGuardrail, buildExpositionContext, buildExpositionExcerpt, buildExtensionPrompt, buildHandoutPrompt, buildHeadingDirective, buildHtmlDeckSlidePrompt, buildImagePrompt, buildJudgePrompt, buildLanguageDirective, buildLessonExcerpt, buildLessonMasterPrompt, buildMarpMarkdownSlidePrompt, buildProjectInstructionPrompt, buildSectionAwareExcerpt, buildSelfLabPrompt, buildSessionSliceContext, buildSlidesPrompt, buildStandardStackMarkdown, buildStandardsContext, buildStandardsContextBlock, buildTeacherGuidePrompt, buildWorksheetPrompt, closeTruncatedJson, computeContentHash, computePackingSpec, conductPreliminaryResearch, contentTools, convertRoadmapToFoundationSot, createAiInferenceError, createContextMissingError, createCurriculumStorage, createIdleAbortController, createStreamAbortController, createStreamAbortSignal, createStreamChunkExtractor, curateMediaLedger, designerTools, detectProjectPedagogy, ensureExpositionForLesson, ensureKnowledgeExposition, evaluateStandardsCoverage, executeCurriculumCommand, executeSlideProductionWorkflow, exportAllProjectQuizzes, expositionCacheKey, extractCurriculumHorizon, extractJsonArray, extractScopeSequenceRows, extractSectionHeadingsFromSLC, extractSessionSlice, extractStandardRefs, extractStreamChunk, extractSymbolLedger, extractThoughtAndContent, findStandardStatement, formatQuizzesToCsv, fulfillMediaLedger, gateModeFor, generateActivityFlow, generateCodeLabFlow, generateDiagnosticQuizFlow, generateEducationalImage, generateExtensionFlow, generateHandoutFlow, generateLessonMasterFlow, generateMilestoneCurriculumBundle, generatePhase1SotArtifacts, generatePhase2SotArtifacts, generateProjectInstruction, generateProjectInstructionFlow, generateRoadmapCurriculum, generateSelfLabFlow, generateSelfPacedBundle, generateSingleArtifact, generateSlidesFlow, generateTeacherGuideFlow, generateWorksheetFlow, getAIModel, getArtifactMetadata, getDesignatedFallbackChain, getDesignatedFallbackConfig, getLessonMetadata, getProjectArtifactScope, getProjectStatusReport, getSlideLayoutPresetById, getSlideLayoutPresetsByCategory, getSmartResumeContext, glossaryTermsForSession, illustratorTools, importRoadmapToProject, ingestProjectGraphIntoProject, initializeProjectInStorage, injectMediaIntoMarkdown, isExpositionFresh, isModelAllowed, isProviderEnabled, isTranslationDue, lintAndSanitizeArtifact, lintCurriculumFramework, lintFrameworkPack, loadCurriculumTemplate, loadSotTemplate, normalizePlanningGraph, normalizeSlcContract, packagerTools, parseAllSessions, parseGateSettings, parseQuizMarkdown, parseRoadmapJsonToProjectPayload, produceBatchLessons, produceSingleLesson, publishToGitHub, publishToSupabase, rankGenCandidates, renderFrameworkFromPlan, renderHorizonPromptBlock, renderMediaPlaceholder, researcherTools, resolveGateSettings, resolveStandardsPacks, resolveStreamBudget, resolveTargetLanguageCode, resolveTranslationTargets, reviewerTools, runCurriculumAIInference, safeParseJson, searchEducationalImages, searchEducationalVideos, selectStatementsForLesson, serializeActivityToMarkdown, serializeCodeLabToMarkdown, serializeDiagnosticQuizToMarkdown, serializeHandoutToMarkdown, serializeLessonToMarkdown, serializeProjectInstructionToMarkdown, serializeSelfLabToMarkdown, slugifyProjectName, streamCurriculumAIInference, streamCurriculumAIInferenceWithTools, streamDiagnosticQuizFlow, streamLLMWithFallback, streamLayerPrefillWithFallback, streamLessonMasterFlow, streamSelfLabFlow, stripLlmJsonWrappers, targetLanguageDisplayName, techSmeTools, topoSort, uploadAssetToBucket, validateArtifactDependencies, validateCurriculumPlan, validateFrameworkPack, validateHorizonCompliance, validateHybridDeckSlides, validateMarkdownTables, validateMermaidSyntax, withAutoRepair };
31119
31719
  //# sourceMappingURL=index.mjs.map
31120
31720
  //# sourceMappingURL=index.mjs.map