tools: the standard argument heal — every registry, both seams
The whitehead incident (2026-10-09): glm-5.3 emitted quiz type with
quotes baked in ('"predict"') — rejected ten times, byte-identical
retries, the lesson never started — and correctAnswer with the JSON
key-value colon glued on ([':parmenides']). Third GLM wobble incident,
and each prior heal was wired only where its incident landed; this one
landed precisely where the practice heal never reached.
Layer 1 — healEncodedArguments moves to core/argument-heal (the display
seam needs it too — the tool-parts law) with withArgumentHeal +
healToolRecord (agent/tools/heal) wired at EVERY registry: prober,
teacher, practice, bareQuestionTools (it had no prepareArguments at
all), demo-builder, and submit-agent (composing after
healStringifiedArrays; the auditor's extraTools wrapped).
Layer 2 — stripModelValueDebris (core/value-debris): leading/trailing
colons, quotes, backticks, whitespace strip at the MEMBERSHIP seams —
coerceCorrectAnswer (boundary check + display + grading in one),
normalizeQuizType (persisted wobbled records keep their role),
resolveTargetId (copy-syntax on targets).
Layer 3 — schema description hygiene: the quiz type and practice
mode/outcome/rung/missClass values now read as bare words — the quoted
JSON-key style invited quote-copying.
Display mirror — parseQuizInput heals the raw record on read, so a
boundary-healed quiz whose record keeps raw args renders instead of
becoming an invisible pending quiz (learner lockout).
Pins: tests/tool-heal.test.ts (the incident shapes end-to-end through
pi-ai validation, the wiring, the strips, the mirror). The stringy-
options pins flip to boundary-parity per tool-parts' own law; the
chat-flow garbage pin keeps its honest-reject intent with an unhealable
shape. 1471 tests, typecheck, lint green.
This commit is contained in:
@@ -14,6 +14,8 @@ import { invalidInputResult, toToolResult } from "../tool-execute.ts";
|
||||
import type { ModelRole } from "../../../config.ts";
|
||||
import type { PiEngine } from "../models.ts";
|
||||
import { runSubagent } from "./pi-run.ts";
|
||||
import { healToolRecord } from "../tools/heal.ts";
|
||||
import { healEncodedArguments } from "../../core/argument-heal.ts";
|
||||
|
||||
// ── the weak-model string-heal (owner, 2026-09-28; moved here from
|
||||
// extraction.ts with the digest-aware redesign, 2026-09-30 — the heal
|
||||
@@ -78,14 +80,23 @@ export async function runSubmitAgent<T>(opts: {
|
||||
let submitted: T | undefined;
|
||||
const result = await runSubagent(
|
||||
[
|
||||
...(opts.extraTools ? Object.values(opts.extraTools) : []),
|
||||
// healToolRecord (owner, 2026-10-09): the standard argument heal
|
||||
// over the extra tools (the auditor's graph set) — same as every
|
||||
// registry.
|
||||
...(opts.extraTools ? Object.values(healToolRecord(opts.extraTools)) : []),
|
||||
{
|
||||
name: opts.toolName,
|
||||
label: opts.toolName,
|
||||
description: opts.toolDescription,
|
||||
parameters: opts.parameters,
|
||||
prepareArguments: (args) =>
|
||||
(opts.prepareArguments ? opts.prepareArguments(args) : args) as never,
|
||||
// The standard heal composes AFTER the caller's shim (the
|
||||
// whitehead generalization, 2026-10-09) — extraction's
|
||||
// healStringifiedArrays stays the inner pass.
|
||||
healEncodedArguments(
|
||||
opts.prepareArguments ? opts.prepareArguments(args) : args,
|
||||
opts.parameters,
|
||||
) as never,
|
||||
execute: async (_toolCallId, input) => {
|
||||
const value = opts.fill(input);
|
||||
const problem = opts.validate?.(value) ?? null;
|
||||
|
||||
Reference in New Issue
Block a user