practice slice 3B — the practice agents: partitioner, generator, grader

src/agent/subagents/practice.ts: createPracticeAgents(deps) with the
ExtractionModels mirror (per-stage engine overrides + engineFor +
providerKeyMissing + refuseIfStagesUnready, all three roles strong-tier).
Generator doctrine = the owner's problem-engineering framework verbatim
(minus Sources) + Part III wiring section per the green-lit amendments
(FULL SET input with minimal-use doctrine; degradation never in
meta-commentary). Partitioner: true-partition doctrine, validate via
chainCoverage + exactly-once + unknown-ids, error-back retries through
runSubmitAgent's new validate hook (ToolInputError → error tool result).
Generator: echo law (chains set === target). Grader: verdict union +
nonempty feedback (schema-enforced). renderChains/renderPropositions/
renderParams/renderPreviousProblems. BACKGROUND_MODEL_ROLES extended with
the practice trio (settings override symmetry). runSubmitAgent gains the
validate seam (null = accept). 16-test faux-engine suite incl. retry
paths, refusal gates, neverSubmitted.
This commit is contained in:
amosblomqvist
2026-09-29 18:14:30 +03:00
parent 00b3c23e06
commit da0cd558d0
7 changed files with 803 additions and 5 deletions
+10 -2
View File
@@ -13,7 +13,7 @@
import type { Usage } from "@earendil-works/pi-ai";
import type { AgentTool } from "@earendil-works/pi-agent-core";
import type { TSchema } from "typebox";
import { toToolResult } from "../tool-execute.ts";
import { toToolResult, ToolInputError } from "../tool-execute.ts";
import type { ModelRole } from "../../../config.ts";
import type { PiEngine } from "../models.ts";
import { runSubagent } from "./pi-run.ts";
@@ -42,6 +42,11 @@ export async function runSubmitAgent<T>(opts: {
* TypeBox validation — the sanctioned shim point). Identity by
* default; extraction wires healStringifiedArrays here. */
prepareArguments?: (args: unknown) => unknown;
/** Semantic validation at the submit seam: the value is schema-valid
* but may violate a PIPELINE law (coverage, echo-equality). Returning
* a problem string throws ToolInputError → pi turns it into an error
* tool result → the model retries within maxTurns. null = accept. */
validate?: (value: T) => string | null;
}): Promise<T> {
let submitted: T | undefined;
const result = await runSubagent(
@@ -55,7 +60,10 @@ export async function runSubmitAgent<T>(opts: {
prepareArguments: (args) =>
(opts.prepareArguments ? opts.prepareArguments(args) : args) as never,
execute: async (_toolCallId, input) => {
submitted = opts.fill(input);
const value = opts.fill(input);
const problem = opts.validate?.(value) ?? null;
if (problem) throw new ToolInputError(opts.toolName, [problem]);
submitted = value;
return toToolResult(submitted);
},
} satisfies AgentTool<any>,