question tools: phase-visible names, hardened teach schemas, context-slim seams
The tools_fails_2 diagnosis (glm-5.3 dropping trailing required fields):
- probe-phase quiz/explain renamed to probe/recall — a shared name carried
the probe call shape into teaching (the first teach quiz dropped nodeId);
display seams accept the old names forever
- teach quiz+explain schemas lead with the required pair (type, nodeId);
explain gains the predict/check type and joins the reveal law (checks
collapse, predictions arrive open; no-type records render check-style)
- plan_init/probe_done results go context-slim (owner, context diet):
content = validated nodeIds + binding-id reminder / fixed ack; details
keeps the tree for the popup and {ok:true} — serializer-verified that
details never reaches the model; failure echoes dropped
- the cards carry the tool's own name (Probe/Recall) and the quiz type
reads on the label (Prediction/Check) in composer and transcript
This commit is contained in:
@@ -202,10 +202,10 @@ describe("the prober's quiz: no nodeId field at all", () => {
|
||||
// Fix wave (2026-09-20): the owner ruling adds research + pdf dispatch
|
||||
// to the probe phase's registry (demo/visual stay teach-only).
|
||||
assert.deepEqual(Object.keys(tools).sort(), [
|
||||
"ask_user_question", "find", "grep", "ls", "pdf", "probe_done", "quiz", "read", "research",
|
||||
"ask_user_question", "find", "grep", "ls", "pdf", "probe", "probe_done", "read", "research",
|
||||
]);
|
||||
const quiz = tools.quiz as AgentTool<any>;
|
||||
assert.equal(Value.Check(quiz.parameters, { ...validQuiz, nodeId: "anything" }), true, "no session check to fail");
|
||||
const probe = tools.probe as AgentTool<any>;
|
||||
assert.equal(Value.Check(probe.parameters, { ...validQuiz, nodeId: "anything" }), true, "no session check to fail");
|
||||
});
|
||||
});
|
||||
// ─── tool availability (2026-09 settings seam) ───────────────────────────────
|
||||
@@ -225,22 +225,22 @@ describe("tool toggles (core/pending.ts + factories)", () => {
|
||||
const chatId = newChat("Prober: toggles");
|
||||
const ctx = { chatId, chatStore, deliver: () => {}, jobStore, spawn: () => {} };
|
||||
assert.deepEqual(Object.keys(createProberTools(ctx)).sort(), [
|
||||
"ask_user_question", "find", "grep", "ls", "pdf", "probe_done", "quiz", "read", "research",
|
||||
"ask_user_question", "find", "grep", "ls", "pdf", "probe", "probe_done", "read", "research",
|
||||
]);
|
||||
|
||||
const noAsk = createProberTools(ctx, { ask_user_question: false });
|
||||
assert.deepEqual(Object.keys(noAsk).sort(), ["find", "grep", "ls", "pdf", "probe_done", "quiz", "read", "research"]);
|
||||
assert.deepEqual(Object.keys(noAsk).sort(), ["find", "grep", "ls", "pdf", "probe", "probe_done", "read", "research"]);
|
||||
|
||||
// Quizzing is mechanics-managed now (owner 2026-09-27): quiz/explain
|
||||
// NEVER pass through the generic filter — hand-written quiz:false is a
|
||||
// no-op (the wedge danger stands: a quiz-less prober collapses its
|
||||
// assessment flow). probe_done is never droppable — it ends the phase.
|
||||
const noQuiz = createProberTools(ctx, { quiz: false });
|
||||
assert.deepEqual(Object.keys(noQuiz).sort(), ["ask_user_question", "find", "grep", "ls", "pdf", "probe_done", "quiz", "read", "research"]);
|
||||
assert.deepEqual(Object.keys(noQuiz).sort(), ["ask_user_question", "find", "grep", "ls", "pdf", "probe", "probe_done", "read", "research"]);
|
||||
// The mechanic switch (owner 2026-09-27): tools.explain === true arms
|
||||
// free-recall — explain in, quiz out, exactly one quizzing tool.
|
||||
const free = createProberTools(ctx, { explain: true });
|
||||
assert.deepEqual(Object.keys(free).sort(), ["ask_user_question", "explain", "find", "grep", "ls", "pdf", "probe_done", "read", "research"]);
|
||||
assert.deepEqual(Object.keys(free).sort(), ["ask_user_question", "find", "grep", "ls", "pdf", "probe_done", "read", "recall", "research"]);
|
||||
assert.ok(createProberTools(ctx, { probe_done: false }).probe_done, "probe_done always-on");
|
||||
});
|
||||
|
||||
@@ -320,6 +320,7 @@ describe("teachExplainToolFor: the explain variant validates nodeId like the qui
|
||||
question: "Explain why the null result matters.",
|
||||
correctAnswer: "It ruled out the aether — a stationary aether predicts a fringe shift, and none was seen.",
|
||||
nodeId: "arith",
|
||||
type: "check",
|
||||
};
|
||||
|
||||
it("accepts a nodeId matching a tree node", async () => {
|
||||
|
||||
Reference in New Issue
Block a user