math: dollar delimiters end-to-end (exact answers, escaping examples, context rendering)
- switch the math authoring/rendering contract from \(...\)/\[...\] to the standard $...$ inline / $$...$$ display: prompts (20-question-tools, 40-formatting), singleDollarTextMath on the renderer, legacy reader kept, and escape-debris recovery extended to dollar spans (mirroring micromark delimiter-run rules) - exact answer membership now precedes debris cleanup in coerceCorrectAnswer (calculus incident: an exact sentence-final-period label was stripped as model debris, then rejected); option keys are passed at the boundary/grading seams, the dock no longer pre-coerces - a complete, phase-correct JSON-escaping example rides the probe/quiz tool descriptions (serialized from one canonical body; quiz leads with type+nodeId), plus the one-sentence JSON-escaping rule in 40-formatting - ask option descriptions render markdown/LaTeX in the pending dock; settled cards keep the details line and option descriptions retroactively, read from the persisted call arguments (chat passes part.input to AnsweredCard) 1566/1566 tests on both navigator configs; typecheck, lint, production build, standalone packaging, and Electron-runtime smoke all green.
This commit is contained in:
@@ -21,13 +21,14 @@ import {
|
||||
createTeacherTools,
|
||||
probeQuizTool,
|
||||
probeExplainTool,
|
||||
quizExampleBody,
|
||||
teachExplainToolFor,
|
||||
teachQuizToolFor,
|
||||
type TeacherToolContext,
|
||||
} from "../src/agent/tools.ts";
|
||||
import { TOGGLEABLE_TOOLS } from "../src/core/pending.ts";
|
||||
import { jobStore } from "../src/store/jobs.ts";
|
||||
import { probeQuizInputSchema, quizInputObject } from "../src/core/question-core.ts";
|
||||
import { probeQuizInputSchema, quizInputObject, quizInputProblems } from "../src/core/question-core.ts";
|
||||
import { chatStore } from "../src/store/chats.ts";
|
||||
import { PATHS } from "../config.ts";
|
||||
|
||||
@@ -208,6 +209,47 @@ describe("the prober's quiz: no nodeId field at all", () => {
|
||||
assert.equal(Value.Check(probe.parameters, { ...validQuiz, nodeId: "anything" }), true, "no session check to fail");
|
||||
});
|
||||
});
|
||||
// ─── the description example (owner, 2026-10-12) ─────────────────────────
|
||||
|
||||
describe("quiz tool descriptions carry the complete, phase-correct example", () => {
|
||||
it("the canonical body is schema-valid and authoring-clean (never ship a broken exemplar)", () => {
|
||||
assert.deepEqual(quizInputProblems(quizExampleBody), []);
|
||||
assert.equal(Value.Check(probeQuizInputSchema, quizExampleBody), true);
|
||||
const teachExample = { type: "check", nodeId: "chain-rule", ...quizExampleBody };
|
||||
assert.equal(Value.Check(quizInputObject, teachExample), true);
|
||||
});
|
||||
|
||||
it("probe: the serialized body rides the description with correct JSON escaping, no phase metadata", () => {
|
||||
const desc = probeQuizTool.description;
|
||||
assert.ok(
|
||||
desc.includes("```json\n" + JSON.stringify(quizExampleBody, null, 2) + "\n```"),
|
||||
"the example is the exact JSON.stringify of the canonical body",
|
||||
);
|
||||
// The escaping exemplar must be VISIBLE as wire text — the runtime
|
||||
// string carries literal double backslashes, exactly what the model
|
||||
// must type inside JSON.
|
||||
assert.ok(desc.includes("A signal is $y=\\\\sin(2x)$"), "LaTeX command backslash shows doubled");
|
||||
assert.ok(desc.includes("a LaTeX backslash is \\\\ and a newline is \\n"), "the one-line escaping hint");
|
||||
assert.ok(!desc.includes("nodeId"), "probe example carries no phase metadata");
|
||||
});
|
||||
|
||||
it("quiz: the required pair leads the example, with the plan-id caveat", () => {
|
||||
const chatId = newChat("Teacher: description example");
|
||||
const desc = teachQuizToolFor({ chatId, chatStore }).description;
|
||||
const teachExample = { type: "check", nodeId: "chain-rule", ...quizExampleBody };
|
||||
assert.ok(desc.includes("```json\n" + JSON.stringify(teachExample, null, 2) + "\n```"));
|
||||
// The 2026-10-09 drop-order ruling: type+nodeId LEAD the object —
|
||||
// fields generated first get dropped least.
|
||||
assert.ok(
|
||||
/```json\n\{\n {2}"type": "check",\n {2}"nodeId": "chain-rule",/.test(desc),
|
||||
"type and nodeId lead the serialized example",
|
||||
);
|
||||
assert.ok(
|
||||
desc.includes("illustrative — copy an exact id from this session's plan_init"),
|
||||
"the illustrative-nodeId caveat",
|
||||
);
|
||||
});
|
||||
});
|
||||
// ─── tool availability (2026-09 settings seam) ───────────────────────────────
|
||||
|
||||
describe("tool toggles (core/pending.ts + factories)", () => {
|
||||
|
||||
Reference in New Issue
Block a user