@thanh01.pmt/curriculum-kit 1.4.22 → 1.4.23
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 +189 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.mjs +189 -4
- package/dist/index.mjs.map +1 -1
- package/dist/workflow/index.cjs.map +1 -1
- package/dist/workflow/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -11624,6 +11624,137 @@ function extractSymbolLedger(lessonMarkdown) {
|
|
|
11624
11624
|
return result;
|
|
11625
11625
|
}
|
|
11626
11626
|
|
|
11627
|
+
// src/services/artifactValidators.ts
|
|
11628
|
+
var CJK_RE = /[\u3400-\u4DBF\u4E00-\u9FFF\u3040-\u30FF\uAC00-\uD7AF]/g;
|
|
11629
|
+
function scanCjkLeaks(content) {
|
|
11630
|
+
const matches = content.match(CJK_RE);
|
|
11631
|
+
if (!matches || matches.length === 0) return null;
|
|
11632
|
+
const lines = [];
|
|
11633
|
+
for (const line of content.split("\n")) {
|
|
11634
|
+
if (CJK_RE.test(line)) lines.push(line.trim().slice(0, 160));
|
|
11635
|
+
}
|
|
11636
|
+
return {
|
|
11637
|
+
code: "cjk-leak",
|
|
11638
|
+
detail: `Ph\xE1t hi\u1EC7n ${matches.length} k\xFD t\u1EF1 CJK (Trung/Nh\u1EADt/H\xE0n) trong artifact ng\xF4n ng\u1EEF Vi\u1EC7t/Anh. B\u1ECB c\u1EA5m tuy\u1EC7t \u0111\u1ED1i.`,
|
|
11639
|
+
evidence: lines.slice(0, 5)
|
|
11640
|
+
};
|
|
11641
|
+
}
|
|
11642
|
+
var VERSION_CLAIM_RE = /\b([A-Z][A-Za-z+#.\-]{1,24})\s+(\d{1,2}(?:\.\d{1,2})?)\b/g;
|
|
11643
|
+
function checkVersionGroundTruth(content, refPack) {
|
|
11644
|
+
if (!refPack || !refPack.trim()) return null;
|
|
11645
|
+
const packLower = refPack.toLowerCase();
|
|
11646
|
+
const claims = /* @__PURE__ */ new Map();
|
|
11647
|
+
for (const m of content.matchAll(VERSION_CLAIM_RE)) {
|
|
11648
|
+
const tool = m[1];
|
|
11649
|
+
if (claims.size === 0 || !claims.has(tool)) claims.set(tool, m[2]);
|
|
11650
|
+
}
|
|
11651
|
+
const contradictions = [];
|
|
11652
|
+
for (const [tool, claimed] of claims) {
|
|
11653
|
+
const toolRe = new RegExp(`\\b${tool.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s+(\\d{1,2}(?:\\.\\d{1,2})?)\\b`, "i");
|
|
11654
|
+
const packMatch = packLower.match(toolRe);
|
|
11655
|
+
if (packMatch && packMatch[1] !== claimed) {
|
|
11656
|
+
contradictions.push(`${tool}: artifact n\xF3i ${claimed}, REFERENCE_PACK ch\u1ED1t ${packMatch[1]}`);
|
|
11657
|
+
}
|
|
11658
|
+
}
|
|
11659
|
+
if (contradictions.length === 0) return null;
|
|
11660
|
+
return {
|
|
11661
|
+
code: "version-contradiction",
|
|
11662
|
+
detail: `Version m\xE2u thu\u1EABn v\u1EDBi ground truth REFERENCE_PACK: ${contradictions.join("; ")}. S\u1EEDa theo b\u1EA3n trong REFERENCE_PACK.`,
|
|
11663
|
+
evidence: contradictions
|
|
11664
|
+
};
|
|
11665
|
+
}
|
|
11666
|
+
function checkScopeDrift(content, scopedKeywords, tolerance = 0) {
|
|
11667
|
+
const conceptHeaders = /* @__PURE__ */ new Set();
|
|
11668
|
+
for (const m of content.matchAll(/\*\*([^*\n]{3,60}?)\s*\((?:WHAT|CIO|SIO|ULO)[^)]*\):\*\*/g)) {
|
|
11669
|
+
conceptHeaders.add(m[1].toLowerCase());
|
|
11670
|
+
}
|
|
11671
|
+
if (conceptHeaders.size === 0) return null;
|
|
11672
|
+
const drift = [];
|
|
11673
|
+
for (const header of conceptHeaders) {
|
|
11674
|
+
const hit = scopedKeywords.some((kw) => {
|
|
11675
|
+
const k = kw.toLowerCase().trim();
|
|
11676
|
+
if (!k) return false;
|
|
11677
|
+
const tokens = k.split(/\s+/);
|
|
11678
|
+
const present = tokens.filter((t) => header.includes(t)).length;
|
|
11679
|
+
return present >= Math.max(1, tokens.length - tolerance);
|
|
11680
|
+
});
|
|
11681
|
+
if (!hit) drift.push(header);
|
|
11682
|
+
}
|
|
11683
|
+
if (drift.length === 0) return null;
|
|
11684
|
+
return {
|
|
11685
|
+
code: "scope-drift",
|
|
11686
|
+
detail: `C\xE1c kh\xE1i ni\u1EC7m sau KH\xD4NG thu\u1ED9c scope c\u1EE7a bu\u1ED5i h\u1ECDc (kh\xF4ng c\xF3 trong new_keywords/graph node keywords): ${drift.join(" | ")}. Xo\xE1 ho\xE0n to\xE0n ho\u1EB7c thay b\u1EB1ng kh\xE1i ni\u1EC7m trong scope.`,
|
|
11687
|
+
evidence: drift
|
|
11688
|
+
};
|
|
11689
|
+
}
|
|
11690
|
+
function validateArtifactDeterministic(input) {
|
|
11691
|
+
const issues = [];
|
|
11692
|
+
const cjk = scanCjkLeaks(input.content);
|
|
11693
|
+
if (cjk) issues.push(cjk);
|
|
11694
|
+
if (input.refPack) {
|
|
11695
|
+
const ver = checkVersionGroundTruth(input.content, input.refPack);
|
|
11696
|
+
if (ver) issues.push(ver);
|
|
11697
|
+
}
|
|
11698
|
+
if (input.scopedKeywords && input.scopedKeywords.length > 0) {
|
|
11699
|
+
const scope = checkScopeDrift(input.content, input.scopedKeywords);
|
|
11700
|
+
if (scope) issues.push(scope);
|
|
11701
|
+
}
|
|
11702
|
+
return { ok: issues.length === 0, issues };
|
|
11703
|
+
}
|
|
11704
|
+
function validatorRepairPrompt(issues) {
|
|
11705
|
+
return [
|
|
11706
|
+
"DETERMINISTIC VALIDATOR FAILURES (mechanically detected \u2014 non-negotiable, fix ALL):",
|
|
11707
|
+
...issues.map((i, n) => `${n + 1}. [${i.code}] ${i.detail}${i.evidence.length ? `
|
|
11708
|
+
Evidence: ${i.evidence.join(" | ").slice(0, 400)}` : ""}`)
|
|
11709
|
+
].join("\n");
|
|
11710
|
+
}
|
|
11711
|
+
|
|
11712
|
+
// src/services/productSpec.ts
|
|
11713
|
+
function extractToolchainFacts(refPack) {
|
|
11714
|
+
const { excerpt } = buildSectionAwareExcerpt(refPack, {
|
|
11715
|
+
priorities: ["Technical Overview & Architecture Blueprint", "Hardware Pinout & Wiring Configuration Matrix"],
|
|
11716
|
+
budget: 4e3
|
|
11717
|
+
});
|
|
11718
|
+
if (!excerpt) return [];
|
|
11719
|
+
const factLines = [];
|
|
11720
|
+
for (const rawLine of excerpt.split("\n")) {
|
|
11721
|
+
const line = rawLine.trim();
|
|
11722
|
+
if (!line || line.startsWith("#")) continue;
|
|
11723
|
+
if (/\d{1,2}(?:\.\d{1,2})?/.test(line) || /Xcode|Swift|macOS|iOS|SDK|Node|Python|JDK|\.NET/i.test(line)) {
|
|
11724
|
+
factLines.push(line.replace(/^[-*•]\s*/, "- "));
|
|
11725
|
+
}
|
|
11726
|
+
if (factLines.length >= 8) break;
|
|
11727
|
+
}
|
|
11728
|
+
return factLines;
|
|
11729
|
+
}
|
|
11730
|
+
function buildProductSpecBlock(input) {
|
|
11731
|
+
const b = input.briefing || {};
|
|
11732
|
+
const platform = (input.overrides?.primaryLanguage || b.coreTechnology || b.techStack || b.topic || "").trim();
|
|
11733
|
+
const hardware = (b.hardwarePlatform || b.studentEquipment || b.classDynamic || "").trim();
|
|
11734
|
+
const toolchainFacts = input.refPack ? extractToolchainFacts(input.refPack) : [];
|
|
11735
|
+
const lines = [];
|
|
11736
|
+
lines.push("[PRODUCT SPEC \u2014 CANONICAL, BINDING FOR EVERY ARTIFACT OF THIS PROJECT]:");
|
|
11737
|
+
lines.push("All decisions below are PROJECT-WIDE. Every artifact (LESSON, satellites, KX)");
|
|
11738
|
+
lines.push("must describe the SAME product. Contradicting or re-deciding any line here is a");
|
|
11739
|
+
lines.push("consistency failure \u2014 if a needed decision is absent, stay GENERIC, never invent.");
|
|
11740
|
+
lines.push("");
|
|
11741
|
+
if (platform) {
|
|
11742
|
+
lines.push(`- Primary technology: ${platform}`);
|
|
11743
|
+
}
|
|
11744
|
+
if (hardware) {
|
|
11745
|
+
lines.push(`- Hardware / classroom setup: ${hardware}`);
|
|
11746
|
+
}
|
|
11747
|
+
if (input.overrides?.deliverableTemplate) {
|
|
11748
|
+
lines.push(`- Deliverable template: ${input.overrides.deliverableTemplate}`);
|
|
11749
|
+
}
|
|
11750
|
+
if (toolchainFacts.length > 0) {
|
|
11751
|
+
lines.push("- Toolchain & versions (ground truth):");
|
|
11752
|
+
for (const f of toolchainFacts) lines.push(` ${f}`);
|
|
11753
|
+
}
|
|
11754
|
+
if (lines.length <= 5) return "";
|
|
11755
|
+
return lines.join("\n");
|
|
11756
|
+
}
|
|
11757
|
+
|
|
11627
11758
|
// src/services/knowledgeExpositionService.ts
|
|
11628
11759
|
var EXPOSITION_REL = (lessonCode) => "_sot/expositions/" + lessonCode + ".md";
|
|
11629
11760
|
var ExpositionApprovalError = class extends Error {
|
|
@@ -11741,7 +11872,12 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11741
11872
|
}
|
|
11742
11873
|
const targetLanguage = options.targetLanguage || plan.translation?.target_language || "vi";
|
|
11743
11874
|
const originalSystemPrompt = buildSystemPrompt(targetLanguage, techStack, hardwarePlatform);
|
|
11744
|
-
const
|
|
11875
|
+
const productSpecBlock = buildProductSpecBlock({
|
|
11876
|
+
refPack,
|
|
11877
|
+
briefing: techStack || hardwarePlatform ? { techStack, hardwarePlatform } : void 0,
|
|
11878
|
+
overrides: options.productSpec
|
|
11879
|
+
});
|
|
11880
|
+
const originalUserPrompt = buildUserPrompt(session, plan, glossary, targetLanguage, options.slcMarkdown, refPack) + (productSpecBlock ? "\n\n" + productSpecBlock : "");
|
|
11745
11881
|
const content = (await llmFn(
|
|
11746
11882
|
originalSystemPrompt,
|
|
11747
11883
|
originalUserPrompt
|
|
@@ -11775,7 +11911,17 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11775
11911
|
const parsed = JSON.parse(match[0]);
|
|
11776
11912
|
return { verdict: parsed.verdict ?? "NEEDS_REVISION", score: parsed.score ?? 0, critique: parsed.critique ?? "" };
|
|
11777
11913
|
};
|
|
11778
|
-
|
|
11914
|
+
const scopedKeywords = [
|
|
11915
|
+
...session.new_keywords,
|
|
11916
|
+
...glossary.map((g) => g.term)
|
|
11917
|
+
];
|
|
11918
|
+
const runDeterministic = (candidate) => validateArtifactDeterministic({
|
|
11919
|
+
content: candidate,
|
|
11920
|
+
refPack,
|
|
11921
|
+
scopedKeywords
|
|
11922
|
+
});
|
|
11923
|
+
let det = runDeterministic(finalContent);
|
|
11924
|
+
let verdict = det.ok ? await judgeOnce(finalContent) : { verdict: "NEEDS_REVISION", score: 0, critique: validatorRepairPrompt(det.issues) };
|
|
11779
11925
|
if (verdict.verdict !== "APPROVED") {
|
|
11780
11926
|
const repairPrompt = [
|
|
11781
11927
|
originalUserPrompt,
|
|
@@ -11783,11 +11929,12 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11783
11929
|
"--- YOUR PREVIOUS DRAFT (REJECTED, fix ALL issues below) ---",
|
|
11784
11930
|
finalContent,
|
|
11785
11931
|
"",
|
|
11786
|
-
"---
|
|
11932
|
+
"--- CRITIQUE (fix ALL issues, output the COMPLETE corrected document) ---",
|
|
11787
11933
|
verdict.critique
|
|
11788
11934
|
].join("\n");
|
|
11789
11935
|
finalContent = (await llmFn(originalSystemPrompt, repairPrompt)).trim();
|
|
11790
|
-
|
|
11936
|
+
det = runDeterministic(finalContent);
|
|
11937
|
+
verdict = det.ok ? await judgeOnce(finalContent) : { verdict: "NEEDS_REVISION", score: 0, critique: validatorRepairPrompt(det.issues) };
|
|
11791
11938
|
}
|
|
11792
11939
|
if (verdict.verdict !== "APPROVED") {
|
|
11793
11940
|
throw new Error("EXPOSITION failed judge for " + lessonCode + " (verdict " + verdict.verdict + ", score " + verdict.score + "): " + verdict.critique);
|
|
@@ -26216,6 +26363,10 @@ ${expositionContext}`);
|
|
|
26216
26363
|
parts.push(`### REFERENCE PACK GROUND TRUTH
|
|
26217
26364
|
${effectiveRefPack}`);
|
|
26218
26365
|
}
|
|
26366
|
+
const productSpec = buildProductSpecBlock({ refPack, briefing: options.briefing });
|
|
26367
|
+
if (productSpec) {
|
|
26368
|
+
parts.push(productSpec);
|
|
26369
|
+
}
|
|
26219
26370
|
return parts.length > 0 ? `
|
|
26220
26371
|
|
|
26221
26372
|
[GROUND TRUTH]:
|
|
@@ -26374,6 +26525,23 @@ ${yamlBlock.trim()}
|
|
|
26374
26525
|
`);
|
|
26375
26526
|
};
|
|
26376
26527
|
var injectYamlReviewMetadata = injectYamlReviewMetadata2;
|
|
26528
|
+
const lessonDet = validateArtifactDeterministic({ content: lessonContent, refPack });
|
|
26529
|
+
if (!lessonDet.ok) {
|
|
26530
|
+
const detCritique = validatorRepairPrompt(lessonDet.issues);
|
|
26531
|
+
onProgress?.("@reviewer", `\u26D4 LESSON deterministic validator FAIL: ${detCritique.slice(0, 200)}`);
|
|
26532
|
+
await storage.updateArtifactState(projectId, lessonCode, "LESSON", {
|
|
26533
|
+
state: "rejected",
|
|
26534
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26535
|
+
contentHash: computeContentHash(lessonContent),
|
|
26536
|
+
review: {
|
|
26537
|
+
decision: "NEEDS_REVISION",
|
|
26538
|
+
reviewedBy: "@heuristic-linter",
|
|
26539
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26540
|
+
score: 0,
|
|
26541
|
+
critique: detCritique
|
|
26542
|
+
}
|
|
26543
|
+
});
|
|
26544
|
+
}
|
|
26377
26545
|
const judgeObjectives = parseLessonObjectives(lessonContent, concept, bloomLevel);
|
|
26378
26546
|
const judgeExpositionExcerpt = expositionContext ? buildExpositionExcerpt(expositionContext, { budget: 3e3, sectionLanguageContract: slcMarkdown, artifactType: "KNOWLEDGE_EXPOSITION" }).excerpt : void 0;
|
|
26379
26547
|
const judgeResult = await LLMJudgeEngine.evaluateArtifactWithLLM({
|
|
@@ -26629,6 +26797,23 @@ ${currentContent}` }],
|
|
|
26629
26797
|
${lessonExcerpt}${symbolLedgerBlock}`;
|
|
26630
26798
|
const judgeSat = (sat, content) => {
|
|
26631
26799
|
if (gateModeFor(gates, sat) !== "LLM_JUDGE") return Promise.resolve();
|
|
26800
|
+
const det = validateArtifactDeterministic({ content, refPack });
|
|
26801
|
+
if (!det.ok) {
|
|
26802
|
+
const critique = validatorRepairPrompt(det.issues);
|
|
26803
|
+
onProgress?.("@reviewer", `\u26D4 ${sat} deterministic validator FAIL: ${critique.slice(0, 200)}`);
|
|
26804
|
+
return storage.updateArtifactState(projectId, lessonCode, sat, {
|
|
26805
|
+
state: "rejected",
|
|
26806
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26807
|
+
contentHash: computeContentHash(content),
|
|
26808
|
+
review: {
|
|
26809
|
+
decision: "NEEDS_REVISION",
|
|
26810
|
+
reviewedBy: "@heuristic-linter",
|
|
26811
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26812
|
+
score: 0,
|
|
26813
|
+
critique
|
|
26814
|
+
}
|
|
26815
|
+
});
|
|
26816
|
+
}
|
|
26632
26817
|
return judgeSatelliteArtifact({
|
|
26633
26818
|
storage,
|
|
26634
26819
|
projectId,
|