fix: address Copilot review round 34 — structured constants, non-required const controls

- A `const` may be an object or an array, with or without a `type`, and
  `String({...})` is "[object Object]" — which no CLI argument can equal, so
  the only value such a schema accepts never matched. Supplied text is parsed
  and compared structurally through the resolver's own comparison (exported as
  `sameJsonValue` so the two cannot disagree), for both branch selection and
  the substitution.
- The TUI's one-option const control is never marked required: its option may
  legitimately be the empty string, which ink-form's required gate can never
  accept, so submission would not even reach `decodeFormValues`. The value is
  fixed by the schema, and `missingRequiredFields` still validates the call.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-26 13:39:16 -04:00
commit ba91db97e2

View file

@ -312,6 +312,28 @@ describe("JSON Utils", () => {
).toEqual({ value: "true" });
});
it("matches a structured const by parsing the supplied text (#2123)", () => {
const structured: Tool = {
name: "structured-const",
inputSchema: {
type: "object",
properties: { tag: { const: { kind: "x", n: 1 } } },
},
};
// `String({...})` is "[object Object]", which no argument can equal — so
// the only value the schema accepts would never have matched.
expect(
convertToolParameters(structured, { tag: '{"n":1,"kind":"x"}' }),
).toEqual({ tag: { kind: "x", n: 1 } });
// Text that is not that value, or not JSON at all, is left alone.
expect(convertToolParameters(structured, { tag: "{}" })).toEqual({
tag: "{}",
});
expect(convertToolParameters(structured, { tag: "nope" })).toEqual({
tag: "nope",
});
});
it("leaves an ambiguously typed argument uncoerced (#2123)", () => {
const ambiguous: Tool = {
name: "ambiguous",