isError results replace thrown ToolInputError (pi 1.0 convention)

Authoring refinements (quizInputProblems, nodeId checks, ask/explain
refinements, submit-agent pipeline laws) now return invalidInputResult —
same model-facing failure and retry semantics pi minted from the throw,
but details keeps {tool, problems} as data instead of the throw path's
empty {}. ToolInputError deleted; wire shape unchanged (verified against
pi's own thrown-error conversion in agent-loop).
This commit is contained in:
amosblomqvist
2026-10-04 20:21:10 +03:00
parent 02e7840b2f
commit d2d159d8ae
6 changed files with 64 additions and 58 deletions
+14 -14
View File
@@ -64,29 +64,29 @@ const validQuiz = {
};
import type { AgentTool } from "@earendil-works/pi-agent-core";
import { ToolInputError } from "../src/agent/tool-execute.ts";
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
/** The Wave A boundary predicate (2026-09-26): pi's loop structurally
* validates against the TypeBox parameters, then execute re-runs the
* authoring refinements (the old superRefine) and throws ToolInputError
* on problems — the model sees both as retryable input errors. This
* helper replicates the old safeParse seam end to end. */
* authoring refinements (the old superRefine) and returns an isError
* result on problems (pi 1.0 convention — invalidInputResult; the model
* sees both as retryable input errors). This helper replicates the old
* safeParse seam end to end. */
async function boundaryParse(
// `input: never` param: contravariance lets any AgentTool's execute slot in.
tool: { parameters: any; execute: (id: string, input: never) => Promise<unknown> },
input: unknown,
): Promise<{ success: boolean; error?: ToolInputError }> {
): Promise<{ success: boolean; error?: AgentToolResult<{ tool: string; problems: string[] }> }> {
if (!Value.Check(tool.parameters, input)) return { success: false };
try {
await tool.execute("tc", input as never);
return { success: true };
} catch (e) {
assert.ok(e instanceof ToolInputError, `expected ToolInputError, got: ${e}`);
return { success: false, error: e };
}
const result = (await tool.execute("tc", input as never)) as AgentToolResult<{
tool: string;
problems: string[];
}>;
if (result.isError) return { success: false, error: result };
return { success: true };
}
const problemLines = (e?: ToolInputError): string[] =>
(e?.message.split("\n") ?? []).filter((l) => l.startsWith("- "));
const problemLines = (r?: AgentToolResult<{ tool: string; problems: string[] }>): string[] =>
r?.details.problems ?? [];
describe("teachQuizToolFor: the dynamic plan-nodeId validation (position signal)", () => {
it("accepts a nodeId that matches a node of the session's tree", async () => {