feat(ai): enabled container-to-string coercion for union branches

- Enable stringification repairs for expected types in union branches.
- Update test to expect successful string coercion for objects in string union branches.
This commit is contained in:
can1357
2026-08-20 06:11:48 +02:00
parent d8cad583e5
commit baf894425c
2 changed files with 12 additions and 19 deletions
+3 -6
View File
@@ -1593,12 +1593,9 @@ function coerceArgsFromIssues(args: unknown, issues: FlatIssue[]): { value: unkn
// a type coercion actually needs to write into a leaf.
let owned = false;
let nextArgs: unknown = args;
for (const issue of issues) {
// Issues surfaced from a failed union branch are guesses from that
// branch's diagnosis, not authoritative: another variant may accept the
// value as-is. Lossy repairs (key deletion, container stringification,
// singleton wrapping) stay off for them; lossless repairs still apply.
// Failed union branches still contribute schema-directed type repairs.
// Container-to-string conversion remains enabled for string branches.
if (issue.keyword === "unrecognized") {
if (issue.unionBranch) continue;
const previous = nextArgs;
@@ -1610,7 +1607,7 @@ function coerceArgsFromIssues(args: unknown, issues: FlatIssue[]): { value: unkn
if (issue.expectedTypes.length === 0) continue;
const currentValue = getValueAtPointer(nextArgs, issue.instancePath);
const result = tryCoerceForExpectedTypes(currentValue, issue.expectedTypes, !issue.unionBranch);
const result = tryCoerceForExpectedTypes(currentValue, issue.expectedTypes, true);
let coercedValue = result.changed ? result.value : undefined;
if (
coercedValue === undefined &&
@@ -56,11 +56,7 @@ describe("Tool argument coercion", () => {
expect(result.payload).toBe('{"a":1,"nested":["x"]}');
});
it("does not stringify container values diagnosed inside a failed union branch", () => {
// Regression: a subagent yield payload with an object in a string-typed
// schema field sat under the tool's `anyOf` wrapper; the stringify repair
// fired on that branch's guess, so validation "passed" and downstream
// consumers received encoded text instead of a retryable error.
it("stringifies container values when a string union branch matches", () => {
const tool: Tool = {
name: "union-string",
description: "",
@@ -74,14 +70,14 @@ describe("Tool argument coercion", () => {
} as never,
};
expect(() =>
validateToolArguments(tool, {
type: "toolCall",
id: "call-union-object",
name: "union-string",
arguments: { payload: { a: 1 } },
}),
).toThrow(/payload/);
const result = validateToolArguments(tool, {
type: "toolCall",
id: "call-union-object",
name: "union-string",
arguments: { payload: { a: 1 } },
}) as { payload: string };
expect(result.payload).toBe('{"a":1}');
});
it("does not delete unrecognized keys diagnosed inside a failed union branch", () => {