@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.d.cts CHANGED
@@ -655,6 +655,9 @@ interface GenerateExpositionOptions {
655
655
  slcMarkdown?: Record<string, Record<string, string>> | string | null;
656
656
  techStack?: string;
657
657
  hardwarePlatform?: string;
658
+ /** REFERENCE_PACK.md raw content — technical ground truth (versions, APIs).
659
+ * 5-Whys RC-vacuum: without it the KX model regresses to pretrained facts. */
660
+ refPack?: string;
658
661
  }
659
662
  interface ExpositionResult {
660
663
  relPath: string;
package/dist/index.d.ts CHANGED
@@ -655,6 +655,9 @@ interface GenerateExpositionOptions {
655
655
  slcMarkdown?: Record<string, Record<string, string>> | string | null;
656
656
  techStack?: string;
657
657
  hardwarePlatform?: string;
658
+ /** REFERENCE_PACK.md raw content — technical ground truth (versions, APIs).
659
+ * 5-Whys RC-vacuum: without it the KX model regresses to pretrained facts. */
660
+ refPack?: string;
658
661
  }
659
662
  interface ExpositionResult {
660
663
  relPath: string;
package/dist/index.mjs CHANGED
@@ -11507,6 +11507,21 @@ function normalizeHeading(str) {
11507
11507
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
11508
11508
  }
11509
11509
  function findCanonicalFuzzy(norm) {
11510
+ 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")) {
11511
+ return "Technical Overview & Architecture Blueprint";
11512
+ }
11513
+ if (norm.includes("pinout") || norm.includes("phan cung") || norm.includes("hardware") || norm.includes("wiring") || norm.includes("ket noi")) {
11514
+ return "Hardware Pinout & Wiring Configuration Matrix";
11515
+ }
11516
+ if (norm.includes("pedagog") || norm.includes("phuong phap") || norm.includes("day hoc") || norm.includes("su pham")) {
11517
+ return "Core Pedagogical Concept Anchor & Real-World Domain Bridge";
11518
+ }
11519
+ if (norm.includes("standard") || norm.includes("tieu chuan") || norm.includes("csta") || norm.includes("cs2023") || norm.includes("chuan academic")) {
11520
+ return "Standards Alignment";
11521
+ }
11522
+ if (norm.includes("roadmap") || norm.includes("lo trinh") || norm.includes("milestone") || norm.includes("giai doan")) {
11523
+ return void 0;
11524
+ }
11510
11525
  if (norm.includes("symbol") || norm.includes("identifier ledger") || norm.includes("dinh danh")) {
11511
11526
  return "Symbol & Identifier Ledger";
11512
11527
  }
@@ -11643,7 +11658,7 @@ function buildSystemPrompt(targetLanguage = "vi", techStack, hardwarePlatform) {
11643
11658
  langDirective
11644
11659
  ].join("\n");
11645
11660
  }
11646
- function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMarkdown) {
11661
+ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMarkdown, refPack) {
11647
11662
  const depthLines = session.depth_assignments.map((d) => "- " + d.node_id + " [" + d.depth.toUpperCase() + "]: " + DEPTH_RULES[d.depth]).join("\n");
11648
11663
  const termLines = glossary.map((g) => "- " + g.term + (g.definition ? " \u2014 " + g.definition : "") + (g.example ? " (example: " + g.example + ")" : "")).join("\n");
11649
11664
  const langCode = resolveTargetLanguageCode(targetLanguage);
@@ -11656,6 +11671,28 @@ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMark
11656
11671
  const hQuestions = headings["Self-Check Questions"] || (langCode === "vi" ? "C\xE2u h\u1ECFi T\u1EF1 ki\u1EC3m tra" : "Self-Check Questions");
11657
11672
  const skeleton = `## ${hScope} / ## ${hTerms} / ## ${hNarratives} / ## ${hExamples} / ## ${hMistakes} / ## ${hQuestions}`;
11658
11673
  const headingDirective = buildHeadingDirective("KNOWLEDGE_EXPOSITION", slcMarkdown, targetLanguage);
11674
+ let refPackBlock = "";
11675
+ if (refPack && refPack.trim()) {
11676
+ const refExcerpt = buildSectionAwareExcerpt(refPack, {
11677
+ priorities: [
11678
+ "Technical Overview & Architecture Blueprint",
11679
+ "Hardware Pinout & Wiring Configuration Matrix",
11680
+ "Standards Alignment"
11681
+ ],
11682
+ budget: 2500
11683
+ }).excerpt;
11684
+ if (refExcerpt) {
11685
+ refPackBlock = [
11686
+ "",
11687
+ "[GROUND TRUTH \u2014 BINDING, from REFERENCE_PACK.md]:",
11688
+ "Tool versions, APIs, and platform facts below are CANONICAL. Do NOT",
11689
+ "contradict them; do NOT substitute versions from memory. If a fact you",
11690
+ "need is not stated here, stay generic rather than inventing specifics.",
11691
+ "",
11692
+ refExcerpt
11693
+ ].join("\n");
11694
+ }
11695
+ }
11659
11696
  return [
11660
11697
  "Session: " + session.id + " \u2014 " + session.title,
11661
11698
  "Objective: " + session.prose_objective,
@@ -11668,6 +11705,7 @@ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMark
11668
11705
  "",
11669
11706
  "Glossary terms (definitions are canonical):",
11670
11707
  termLines || "(none provided \u2014 write definitions and mark them for glossary sync)",
11708
+ refPackBlock,
11671
11709
  "",
11672
11710
  "Section skeleton to fill (replace ONLY the {{placeholders}}):",
11673
11711
  skeleton,
@@ -11676,7 +11714,7 @@ function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMark
11676
11714
  ].filter(Boolean).join("\n");
11677
11715
  }
11678
11716
  async function ensureKnowledgeExposition(options) {
11679
- const { projectId, lessonCode, plan, glossary, llmFn, storage, techStack, hardwarePlatform } = options;
11717
+ const { projectId, lessonCode, plan, glossary, llmFn, storage, techStack, hardwarePlatform, refPack } = options;
11680
11718
  const session = plan.sessions.find((s) => s.id === lessonCode);
11681
11719
  if (!session) throw new Error("Session " + lessonCode + " not found in plan " + plan.plan_id);
11682
11720
  const existing = await storage.readArtifact(projectId, EXPOSITION_REL(lessonCode));
@@ -11690,9 +11728,11 @@ async function ensureKnowledgeExposition(options) {
11690
11728
  throw new ExpositionApprovalError("Approval hash " + plan.approval.plan_hash + " does not match current plan hash " + plan.plan_hash + " \u2014 re-approve after plan changes");
11691
11729
  }
11692
11730
  const targetLanguage = options.targetLanguage || plan.translation?.target_language || "vi";
11731
+ const originalSystemPrompt = buildSystemPrompt(targetLanguage, techStack, hardwarePlatform);
11732
+ const originalUserPrompt = buildUserPrompt(session, plan, glossary, targetLanguage, options.slcMarkdown, refPack);
11693
11733
  const content = (await llmFn(
11694
- buildSystemPrompt(targetLanguage, techStack, hardwarePlatform),
11695
- buildUserPrompt(session, plan, glossary, targetLanguage, options.slcMarkdown)
11734
+ originalSystemPrompt,
11735
+ originalUserPrompt
11696
11736
  )).trim();
11697
11737
  if (content.length < 200) {
11698
11738
  throw new Error("EXPOSITION too short for " + lessonCode + " (" + content.length + " chars) \u2014 refusing to save");
@@ -11725,8 +11765,16 @@ async function ensureKnowledgeExposition(options) {
11725
11765
  };
11726
11766
  let verdict = await judgeOnce(finalContent);
11727
11767
  if (verdict.verdict !== "APPROVED") {
11728
- const repairPrompt = finalContent + "\n\nJUDGE CRITIQUE (fix ALL issues, output the COMPLETE corrected document):\n" + verdict.critique;
11729
- finalContent = (await llmFn(buildSystemPrompt(), repairPrompt)).trim();
11768
+ const repairPrompt = [
11769
+ originalUserPrompt,
11770
+ "",
11771
+ "--- YOUR PREVIOUS DRAFT (REJECTED, fix ALL issues below) ---",
11772
+ finalContent,
11773
+ "",
11774
+ "--- JUDGE CRITIQUE (fix ALL issues, output the COMPLETE corrected document) ---",
11775
+ verdict.critique
11776
+ ].join("\n");
11777
+ finalContent = (await llmFn(originalSystemPrompt, repairPrompt)).trim();
11730
11778
  verdict = await judgeOnce(finalContent);
11731
11779
  }
11732
11780
  if (verdict.verdict !== "APPROVED") {
@@ -11767,12 +11815,27 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
11767
11815
  try {
11768
11816
  const lpRaw = await storage.readSotDocument(projectId, "LEARNER_PROFILE.md");
11769
11817
  if (lpRaw) {
11770
- const hwMatch = lpRaw.match(/(?:Student Equipment|Hardware|Platform|Thiết bị)[:\s*`]+([^\n\r]+)/i);
11771
- if (hwMatch && !hw) hw = hwMatch[1].trim();
11818
+ const hwPatterns = [
11819
+ /(?:Student Equipment|Hardware|Platform|Thiết bị|Thiết bị học tập)\s*(?:\(|:|\*)*\s*([^\n\r]+?)(?:\s*\*\*|[.).]?\s*$)/i,
11820
+ /(?:máy|device|machine)\s+([^,.;\n]{4,60}(?:M\d|Intel|PC|computer|mini)[^,.;\n]{0,30})/i
11821
+ ];
11822
+ for (const p of hwPatterns) {
11823
+ const m = lpRaw.match(p);
11824
+ if (m && !hw) {
11825
+ hw = m[1].trim();
11826
+ break;
11827
+ }
11828
+ }
11772
11829
  }
11773
11830
  } catch {
11774
11831
  }
11775
11832
  }
11833
+ let refPackContent;
11834
+ try {
11835
+ refPackContent = await storage.readSotDocument(projectId, "REFERENCE_PACK.md") || void 0;
11836
+ } catch {
11837
+ refPackContent = void 0;
11838
+ }
11776
11839
  const { runCurriculumAIInference: runCurriculumAIInference2 } = await Promise.resolve().then(() => (init_streamRunner(), streamRunner_exports));
11777
11840
  const targetLang = options.targetLanguage || plan.translation?.target_language || "vi";
11778
11841
  const result = await ensureKnowledgeExposition({
@@ -11784,6 +11847,7 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
11784
11847
  slcMarkdown: options.slcMarkdown,
11785
11848
  techStack: tech,
11786
11849
  hardwarePlatform: hw,
11850
+ refPack: refPackContent,
11787
11851
  llmFn: async (systemPrompt, userPrompt) => {
11788
11852
  const runnerOpts = options.customInference ? { customInference: options.customInference } : {};
11789
11853
  const out = await runCurriculumAIInference2(
@@ -26082,11 +26146,16 @@ async function produceSingleLesson(storage, projectId, lessonId, options = {}) {
26082
26146
  }).excerpt : "";
26083
26147
  let effectiveRefPack = refPack ? buildSectionAwareExcerpt(refPack, {
26084
26148
  priorities: [
26149
+ // Toolchain/version matrix FIRST — the most-frequently-hallucinated
26150
+ // facts are tool versions ("Xcode 15" vs ground truth "Xcode 16").
26151
+ // The bilingual fuzzy matcher in contextBuilder resolves these keys
26152
+ // even when the generated RefPack headings are Vietnamese.
26085
26153
  "Technical Overview & Architecture Blueprint",
26086
26154
  "Hardware Pinout & Wiring Configuration Matrix",
26155
+ "Standards Alignment",
26087
26156
  "Core Pedagogical Concept Anchor & Real-World Domain Bridge"
26088
26157
  ],
26089
- budget: 2500
26158
+ budget: 3500
26090
26159
  }).excerpt : "";
26091
26160
  const baseContextPrefix = `PROJECT & ACADEMIC CONTEXT:
26092
26161
  - Project ID: ${projectId}