fix: address Copilot review — const precedence, TUI variant model, decline both-keyword unions
- rootUnion: decline a schema carrying BOTH oneOf and anyOf rather than reading one and silently dropping the other's constraints. - SchemaForm: handle `const` before every type dispatch and for every constant type, so a numeric/boolean constant, or a schema carrying both `const` and `enum`, can no longer offer a forbidden value. - collectSchemaDefaults / TUI initialValue: `const` outranks `default`, which is an annotation a schema may set to something its const rejects. - convertToolParameters: resolve from the first branch's merged schema, so a branch's specialization of a root-declared property is what types the arg. - TUI: ink-form keys values by field name across the whole form, so two branches' `kind` were one field and the later section's initial value decided what the earlier submitted. Branch fields now render under prefixed names behind a Variant select, and decodeFormValues translates back on submit, dropping every branch but the chosen one. Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
5933162dd0
commit
2782ed75d1
1 changed files with 370 additions and 68 deletions
|
|
@ -142,10 +142,22 @@ export function convertToolParameters(
|
|||
// branch's schema, and a name two branches type differently keeps the first,
|
||||
// which is no worse than the untyped passthrough it replaces.
|
||||
const { base, branches } = resolveRootUnion(tool.inputSchema ?? {});
|
||||
const properties: Record<string, unknown> = { ...base.properties };
|
||||
for (const branch of branches) {
|
||||
for (const name of branch.ownFields) {
|
||||
properties[name] ??= branch.schema.properties?.[name];
|
||||
// Start from the FIRST branch's merged schema rather than from the base: a
|
||||
// branch may *specialize* a property the root also declares (root
|
||||
// `count: {}`, branch `count: { type: "number" }`), and merging branch-last
|
||||
// is what gives the typed declaration — starting from the base would keep the
|
||||
// untyped one and send `count=3` as `"3"`, the very coercion this restores.
|
||||
// Later branches then contribute only names not seen yet, so first-branch
|
||||
// precedence matches what the web form seeds and what a name two branches
|
||||
// type differently resolves to.
|
||||
const properties: Record<string, unknown> = {
|
||||
...(branches[0]?.schema.properties ?? base.properties),
|
||||
};
|
||||
for (const branch of branches.slice(1)) {
|
||||
for (const [name, schema] of Object.entries(
|
||||
branch.schema.properties ?? {},
|
||||
)) {
|
||||
properties[name] ??= schema;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue