fix(tool-todo): declare the unknown-key rejection in the item schema
additionalProperties stays true in the published schema while execute rejected extra keys, so generated typings and validation disagreed with runtime behavior. The item schema now declares additionalProperties: false — the registry's arg validation rejects extra keys with a path-qualified violation before execute runs — and the redundant manual check is dropped (tool catalog regenerated).
This commit is contained in:
@@ -28,21 +28,17 @@ const DESCRIPTION =
|
||||
|
||||
/**
|
||||
* Validate the value constraints the ParameterSchemaSpec can't express and build the canonical {@link
|
||||
* TodoItem}[]: known keys only, trimmed non-empty unique content, and at most one in-progress
|
||||
* item. The registry has already enforced the status enum; the cast below records that
|
||||
* guarantee. Unknown keys are rejected rather than dropped — the logged snapshot must equal
|
||||
* what the model believes it wrote (model-visible ⟺ logged), so a nested/extended item shape
|
||||
* fails loud instead of silently flattening.
|
||||
* TodoItem}[]: trimmed non-empty unique content and at most one in-progress item. The registry
|
||||
* has already enforced the status enum and rejected unknown item keys (`additionalProperties:
|
||||
* false` — the logged snapshot must equal what the model believes it wrote, so a nested/extended
|
||||
* item shape fails loud at the schema boundary instead of silently flattening); the cast below
|
||||
* records that guarantee.
|
||||
*/
|
||||
function toTodoList(raw: { content: string; status: string }[]): TodoItem[] {
|
||||
const todos: TodoItem[] = []
|
||||
const seen = new Set<string>()
|
||||
let inProgress = 0
|
||||
for (const item of raw) {
|
||||
const unknown = Object.keys(item).filter(key => key !== 'content' && key !== 'status')
|
||||
if (unknown.length > 0) {
|
||||
throw new Error(`invalid todo: unknown key(s) ${unknown.map(k => JSON.stringify(k)).join(', ')} — each item is exactly { content, status }`)
|
||||
}
|
||||
const content = item.content.trim()
|
||||
if (content.length === 0) {
|
||||
throw new Error('invalid todo: `content` must be a non-empty string')
|
||||
@@ -73,7 +69,7 @@ export function apply(ctx: Context): void {
|
||||
description: 'The COMPLETE task list, replacing any previous list.',
|
||||
items: {
|
||||
type: 'object',
|
||||
additionalProperties: true,
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
content: { type: 'string', required: true, description: 'What the task is — a short imperative line.' },
|
||||
status: {
|
||||
|
||||
Reference in New Issue
Block a user