Make task schema fields explicit with null sentinels
This commit is contained in:
+85
-1
@@ -1,5 +1,6 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { validateToolArguments } from "../node_modules/@earendil-works/pi-ai/dist/utils/validation.js";
|
||||
import { TaskState } from "../src/state.ts";
|
||||
import { registerTaskTools } from "../src/tools.ts";
|
||||
|
||||
@@ -18,7 +19,9 @@ function registeredTools(state = new TaskState()) {
|
||||
}
|
||||
|
||||
async function execute(tool: any, params: unknown) {
|
||||
return tool.execute("call", params, undefined, undefined, {});
|
||||
const prepared = tool.prepareArguments?.(params) ?? params;
|
||||
const validated = validateToolArguments(tool, { name: tool.name, arguments: prepared } as never);
|
||||
return tool.execute("call", validated, undefined, undefined, {});
|
||||
}
|
||||
|
||||
test("registers five focused tools without the old add_task alias", () => {
|
||||
@@ -38,6 +41,8 @@ test("set_tasks schema hoists $defs to the root with #/$defs/ refs", () => {
|
||||
// $defs must live at the document root: root-style refs resolve from there,
|
||||
// and moonshot-flavored validators reject nested definitions (Console Go).
|
||||
assert.ok(params.$defs.AddTask, "$defs hoisted to the parameter schema root");
|
||||
assert.deepEqual(params.required, ["mode", "tasks", "force"]);
|
||||
assert.deepEqual(params.$defs.AddTask.required, ["name", "description", "status", "parentId", "children"]);
|
||||
assert.equal(itemSchema.$defs, undefined, "no $defs nested inside tasks items");
|
||||
assert.equal(itemSchema.$ref, "#/$defs/AddTask");
|
||||
assert.equal(params.$defs.AddTask.properties.children.items.$ref, "#/$defs/AddTask");
|
||||
@@ -46,6 +51,85 @@ test("set_tasks schema hoists $defs to the root with #/$defs/ refs", () => {
|
||||
assert.ok(!/"\$ref":"(?!\#)/.test(serialized), "no bare $ref values remain");
|
||||
});
|
||||
|
||||
test("strict task schemas normalize legacy optional arguments", () => {
|
||||
const { tools } = registeredTools();
|
||||
const setTool = tools.get("set_tasks");
|
||||
const updateTool = tools.get("update_task");
|
||||
|
||||
assert.deepEqual(updateTool.parameters.properties.tasks.items.required, [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
"status",
|
||||
"parentId",
|
||||
"position",
|
||||
]);
|
||||
assert.deepEqual(setTool.prepareArguments({
|
||||
mode: "append",
|
||||
tasks: [{ name: "Parent", children: [{ name: "Child" }] }],
|
||||
}), {
|
||||
mode: "append",
|
||||
tasks: [
|
||||
{
|
||||
name: "Parent",
|
||||
description: null,
|
||||
status: "pending",
|
||||
parentId: null,
|
||||
children: [
|
||||
{
|
||||
name: "Child",
|
||||
description: null,
|
||||
status: "pending",
|
||||
parentId: null,
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
force: false,
|
||||
});
|
||||
assert.deepEqual(updateTool.prepareArguments({ tasks: [{ id: 1, status: "done" }] }), {
|
||||
tasks: [
|
||||
{
|
||||
id: 1,
|
||||
name: null,
|
||||
description: null,
|
||||
status: "done",
|
||||
parentId: -1,
|
||||
position: -1,
|
||||
},
|
||||
],
|
||||
});
|
||||
assert.equal(updateTool.prepareArguments({ tasks: [{ id: 1, parentId: null }] }).tasks[0].parentId, 0);
|
||||
|
||||
const validated = validateToolArguments(updateTool, {
|
||||
name: "update_task",
|
||||
arguments: updateTool.prepareArguments({ tasks: [{ id: 1, status: "done" }] }),
|
||||
} as never);
|
||||
assert.equal(validated.tasks[0].parentId, -1);
|
||||
assert.equal(validated.tasks[0].position, -1);
|
||||
});
|
||||
|
||||
test("validated update sentinels preserve order and map legacy null parent to root", async () => {
|
||||
const harness = registeredTools();
|
||||
await execute(harness.tools.get("set_tasks"), {
|
||||
mode: "append",
|
||||
tasks: [
|
||||
{ name: "Parent", children: [{ name: "Child" }] },
|
||||
{ name: "Sibling" },
|
||||
],
|
||||
});
|
||||
|
||||
await execute(harness.tools.get("update_task"), { tasks: [{ id: 3, status: "blocked" }] });
|
||||
assert.deepEqual(
|
||||
harness.state.list().filter((task) => task.parentId === null).map((task) => task.id),
|
||||
[1, 3],
|
||||
);
|
||||
|
||||
await execute(harness.tools.get("update_task"), { tasks: [{ id: 2, parentId: null }] });
|
||||
assert.equal(harness.state.get([2])[0]?.parentId, null);
|
||||
});
|
||||
|
||||
test("set_tasks appends optional-description tasks and persists a complete snapshot", async () => {
|
||||
const harness = registeredTools();
|
||||
const result = await execute(harness.tools.get("set_tasks"), {
|
||||
|
||||
Reference in New Issue
Block a user