@thanh01.pmt/curriculum-kit 1.4.21 → 1.4.22

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.cjs CHANGED
@@ -11519,6 +11519,21 @@ function normalizeHeading(str) {
11519
11519
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
11520
11520
  }
11521
11521
  function findCanonicalFuzzy(norm) {
11522
+ if (norm.includes("toolchain") || norm.includes("moi truong phat trien") || norm.includes("cong cu") || norm.includes("version") || norm.includes("phan mem") || norm.includes("development environment") || norm.includes("technical overview") || norm.includes("kien truc")) {
11523
+ return "Technical Overview & Architecture Blueprint";
11524
+ }
11525
+ if (norm.includes("pinout") || norm.includes("phan cung") || norm.includes("hardware") || norm.includes("wiring") || norm.includes("ket noi")) {
11526
+ return "Hardware Pinout & Wiring Configuration Matrix";
11527
+ }
11528
+ if (norm.includes("pedagog") || norm.includes("phuong phap") || norm.includes("day hoc") || norm.includes("su pham")) {
11529
+ return "Core Pedagogical Concept Anchor & Real-World Domain Bridge";
11530
+ }
11531
+ if (norm.includes("standard") || norm.includes("tieu chuan") || norm.includes("csta") || norm.includes("cs2023") || norm.includes("chuan academic")) {
11532
+ return "Standards Alignment";
11533
+ }
11534
+ if (norm.includes("roadmap") || norm.includes("lo trinh") || norm.includes("milestone") || norm.includes("giai doan")) {
11535
+ return void 0;
11536
+ }
11522
11537
  if (norm.includes("symbol") || norm.includes("identifier ledger") || norm.includes("dinh danh")) {
11523
11538
  return "Symbol & Identifier Ledger";
11524
11539
  }
@@ -11655,7 +11670,7 @@ function buildSystemPrompt(targetLanguage = "vi", techStack, hardwarePlatform) {
11655
11670
  langDirective
11656
11671
  ].join("\n");
11657
11672
  }
11658
- function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMarkdown) {
11673
+ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMarkdown, refPack) {
11659
11674
  const depthLines = session.depth_assignments.map((d) => "- " + d.node_id + " [" + d.depth.toUpperCase() + "]: " + DEPTH_RULES[d.depth]).join("\n");
11660
11675
  const termLines = glossary.map((g) => "- " + g.term + (g.definition ? " \u2014 " + g.definition : "") + (g.example ? " (example: " + g.example + ")" : "")).join("\n");
11661
11676
  const langCode = resolveTargetLanguageCode(targetLanguage);
@@ -11668,6 +11683,28 @@ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMark
11668
11683
  const hQuestions = headings["Self-Check Questions"] || (langCode === "vi" ? "C\xE2u h\u1ECFi T\u1EF1 ki\u1EC3m tra" : "Self-Check Questions");
11669
11684
  const skeleton = `## ${hScope} / ## ${hTerms} / ## ${hNarratives} / ## ${hExamples} / ## ${hMistakes} / ## ${hQuestions}`;
11670
11685
  const headingDirective = buildHeadingDirective("KNOWLEDGE_EXPOSITION", slcMarkdown, targetLanguage);
11686
+ let refPackBlock = "";
11687
+ if (refPack && refPack.trim()) {
11688
+ const refExcerpt = buildSectionAwareExcerpt(refPack, {
11689
+ priorities: [
11690
+ "Technical Overview & Architecture Blueprint",
11691
+ "Hardware Pinout & Wiring Configuration Matrix",
11692
+ "Standards Alignment"
11693
+ ],
11694
+ budget: 2500
11695
+ }).excerpt;
11696
+ if (refExcerpt) {
11697
+ refPackBlock = [
11698
+ "",
11699
+ "[GROUND TRUTH \u2014 BINDING, from REFERENCE_PACK.md]:",
11700
+ "Tool versions, APIs, and platform facts below are CANONICAL. Do NOT",
11701
+ "contradict them; do NOT substitute versions from memory. If a fact you",
11702
+ "need is not stated here, stay generic rather than inventing specifics.",
11703
+ "",
11704
+ refExcerpt
11705
+ ].join("\n");
11706
+ }
11707
+ }
11671
11708
  return [
11672
11709
  "Session: " + session.id + " \u2014 " + session.title,
11673
11710
  "Objective: " + session.prose_objective,
@@ -11680,6 +11717,7 @@ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMark
11680
11717
  "",
11681
11718
  "Glossary terms (definitions are canonical):",
11682
11719
  termLines || "(none provided \u2014 write definitions and mark them for glossary sync)",
11720
+ refPackBlock,
11683
11721
  "",
11684
11722
  "Section skeleton to fill (replace ONLY the {{placeholders}}):",
11685
11723
  skeleton,
@@ -11688,7 +11726,7 @@ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMark
11688
11726
  ].filter(Boolean).join("\n");
11689
11727
  }
11690
11728
  async function ensureKnowledgeExposition(options) {
11691
- const { projectId, lessonCode, plan, glossary, llmFn, storage, techStack, hardwarePlatform } = options;
11729
+ const { projectId, lessonCode, plan, glossary, llmFn, storage, techStack, hardwarePlatform, refPack } = options;
11692
11730
  const session = plan.sessions.find((s) => s.id === lessonCode);
11693
11731
  if (!session) throw new Error("Session " + lessonCode + " not found in plan " + plan.plan_id);
11694
11732
  const existing = await storage.readArtifact(projectId, EXPOSITION_REL(lessonCode));
@@ -11702,9 +11740,11 @@ async function ensureKnowledgeExposition(options) {
11702
11740
  throw new ExpositionApprovalError("Approval hash " + plan.approval.plan_hash + " does not match current plan hash " + plan.plan_hash + " \u2014 re-approve after plan changes");
11703
11741
  }
11704
11742
  const targetLanguage = options.targetLanguage || plan.translation?.target_language || "vi";
11743
+ const originalSystemPrompt = buildSystemPrompt(targetLanguage, techStack, hardwarePlatform);
11744
+ const originalUserPrompt = buildUserPrompt(session, plan, glossary, targetLanguage, options.slcMarkdown, refPack);
11705
11745
  const content = (await llmFn(
11706
- buildSystemPrompt(targetLanguage, techStack, hardwarePlatform),
11707
- buildUserPrompt(session, plan, glossary, targetLanguage, options.slcMarkdown)
11746
+ originalSystemPrompt,
11747
+ originalUserPrompt
11708
11748
  )).trim();
11709
11749
  if (content.length < 200) {
11710
11750
  throw new Error("EXPOSITION too short for " + lessonCode + " (" + content.length + " chars) \u2014 refusing to save");
@@ -11737,8 +11777,16 @@ async function ensureKnowledgeExposition(options) {
11737
11777
  };
11738
11778
  let verdict = await judgeOnce(finalContent);
11739
11779
  if (verdict.verdict !== "APPROVED") {
11740
- const repairPrompt = finalContent + "\n\nJUDGE CRITIQUE (fix ALL issues, output the COMPLETE corrected document):\n" + verdict.critique;
11741
- finalContent = (await llmFn(buildSystemPrompt(), repairPrompt)).trim();
11780
+ const repairPrompt = [
11781
+ originalUserPrompt,
11782
+ "",
11783
+ "--- YOUR PREVIOUS DRAFT (REJECTED, fix ALL issues below) ---",
11784
+ finalContent,
11785
+ "",
11786
+ "--- JUDGE CRITIQUE (fix ALL issues, output the COMPLETE corrected document) ---",
11787
+ verdict.critique
11788
+ ].join("\n");
11789
+ finalContent = (await llmFn(originalSystemPrompt, repairPrompt)).trim();
11742
11790
  verdict = await judgeOnce(finalContent);
11743
11791
  }
11744
11792
  if (verdict.verdict !== "APPROVED") {
@@ -11779,12 +11827,27 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
11779
11827
  try {
11780
11828
  const lpRaw = await storage.readSotDocument(projectId, "LEARNER_PROFILE.md");
11781
11829
  if (lpRaw) {
11782
- const hwMatch = lpRaw.match(/(?:Student Equipment|Hardware|Platform|Thiết bị)[:\s*`]+([^\n\r]+)/i);
11783
- if (hwMatch && !hw) hw = hwMatch[1].trim();
11830
+ const hwPatterns = [
11831
+ /(?:Student Equipment|Hardware|Platform|Thiết bị|Thiết bị học tập)\s*(?:\(|:|\*)*\s*([^\n\r]+?)(?:\s*\*\*|[.).]?\s*$)/i,
11832
+ /(?:máy|device|machine)\s+([^,.;\n]{4,60}(?:M\d|Intel|PC|computer|mini)[^,.;\n]{0,30})/i
11833
+ ];
11834
+ for (const p of hwPatterns) {
11835
+ const m = lpRaw.match(p);
11836
+ if (m && !hw) {
11837
+ hw = m[1].trim();
11838
+ break;
11839
+ }
11840
+ }
11784
11841
  }
11785
11842
  } catch {
11786
11843
  }
11787
11844
  }
11845
+ let refPackContent;
11846
+ try {
11847
+ refPackContent = await storage.readSotDocument(projectId, "REFERENCE_PACK.md") || void 0;
11848
+ } catch {
11849
+ refPackContent = void 0;
11850
+ }
11788
11851
  const { runCurriculumAIInference: runCurriculumAIInference2 } = await Promise.resolve().then(() => (init_streamRunner(), streamRunner_exports));
11789
11852
  const targetLang = options.targetLanguage || plan.translation?.target_language || "vi";
11790
11853
  const result = await ensureKnowledgeExposition({
@@ -11796,6 +11859,7 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
11796
11859
  slcMarkdown: options.slcMarkdown,
11797
11860
  techStack: tech,
11798
11861
  hardwarePlatform: hw,
11862
+ refPack: refPackContent,
11799
11863
  llmFn: async (systemPrompt, userPrompt) => {
11800
11864
  const runnerOpts = options.customInference ? { customInference: options.customInference } : {};
11801
11865
  const out = await runCurriculumAIInference2(
@@ -26094,11 +26158,16 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
26094
26158
  }).excerpt : "";
26095
26159
  let effectiveRefPack = refPack ? buildSectionAwareExcerpt(refPack, {
26096
26160
  priorities: [
26161
+ // Toolchain/version matrix FIRST — the most-frequently-hallucinated
26162
+ // facts are tool versions ("Xcode 15" vs ground truth "Xcode 16").
26163
+ // The bilingual fuzzy matcher in contextBuilder resolves these keys
26164
+ // even when the generated RefPack headings are Vietnamese.
26097
26165
  "Technical Overview & Architecture Blueprint",
26098
26166
  "Hardware Pinout & Wiring Configuration Matrix",
26167
+ "Standards Alignment",
26099
26168
  "Core Pedagogical Concept Anchor & Real-World Domain Bridge"
26100
26169
  ],
26101
- budget: 2500
26170
+ budget: 3500
26102
26171
  }).excerpt : "";
26103
26172
  const baseContextPrefix = `PROJECT & ACADEMIC CONTEXT:
26104
26173
  - Project ID: ${projectId}