fix(coding-agent/tools): removed the enforced minimum length for the

- Removed the enforced minimum length for the `items` array in the tool schema to prevent unnecessary validation errors on operations that ignore the field.
- Added tests to verify schema acceptance of empty `items` arrays and confirmed that runtime errors are still correctly thrown for empty inputs during specific operations like `append`.
This commit is contained in:
can1357
2026-06-27 00:44:35 +02:00
parent b612e8c792
commit 53af82d539
2 changed files with 28 additions and 1 deletions
+4 -1
View File
@@ -57,7 +57,10 @@ const todoSchema = type({
"list?": InitListEntry.array().describe("phased task list (init)"),
"task?": type("string").describe("task content"),
"phase?": type("string").describe("phase name"),
"items?": type("string").describe("task content").array().atLeastLength(1).describe("tasks to append"),
// No `atLeastLength(1)` here: `items` is only meaningful for `init`/`append`,
// and both enforce non-empty with op-specific errors. A stray `items: []` on
// an op that ignores it (e.g. `view`) must not be a hard schema rejection.
"items?": type("string").describe("task content").array().describe("tasks to append"),
}).describe("apply a single todo operation");
type TodoParams = TodoSchema;
@@ -15,6 +15,7 @@ import {
todoToolRenderer,
} from "@oh-my-pi/pi-coding-agent/tools";
import type { Component } from "@oh-my-pi/pi-tui";
import { type } from "arktype";
function createSession(initialPhases: TodoPhase[] = []): ToolSession {
let phases = initialPhases;
@@ -284,6 +285,29 @@ describe("TodoTool lenient init shapes", () => {
});
});
describe("TodoTool empty items tolerance", () => {
// Regression: a stray `items: []` on an op that ignores items (here `view`)
// must not be a hard schema rejection. The top-level `items` array dropped
// its `atLeastLength(1)` so callers don't get "items must be tasks to append"
// for an irrelevant empty array; length is enforced per-op at runtime.
it("accepts op:view with an empty items array at the schema boundary", () => {
const schema = new TodoTool(createSession()).parameters;
expect(schema({ op: "view", items: [] }) instanceof type.errors).toBe(false);
});
it("defers empty append items to an op-specific runtime error", async () => {
const tool = new TodoTool(createSession());
await tool.execute("call-1", { op: "init", list: [{ phase: "Work", items: ["First"] }] });
const result = await tool.execute("call-2", { op: "append", phase: "Work", items: [] });
expect(result.isError).toBe(true);
const summary = result.content.find(part => part.type === "text");
if (summary?.type !== "text") throw new Error("Expected text summary");
expect(summary.text).toContain("Missing items for append operation");
});
});
describe("selectStickyTodoWindow", () => {
const makeTasks = (statuses: TodoStatus[]): TodoItem[] =>
statuses.map((status, i) => ({ content: `task-${i + 1}`, status }));