fix(ai-tools): make navigate_app tool schema a valid object root for direct model binding (#22284)

## Problem

Using an AI Agent step in a workflow fails with:

> Invalid schema for function 'navigate_app': schema must be a JSON
Schema of 'type: "object"', got 'type: "None"'.

## Root cause

The `navigate_app` tool declared its `inputSchema` as a top-level
`z.discriminatedUnion('type', [...])`. When serialized via
`toToolJsonSchema`, a discriminated union produces a **root-level
`anyOf`** with no top-level `"type"`:

```json
{ "anyOf": [ { "type": "object", ... }, ... ] }

<!-- This is an auto-generated description by cubic. -->
<a href="https://cubic.dev/pr/twentyhq/twenty/pull/22284?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Etienne
2026-06-29 11:03:16 +02:00
committed by GitHub
parent 71370d9e66
commit 02d62c4175
2 changed files with 9 additions and 2 deletions
@@ -1,6 +1,6 @@
import { z } from 'zod';
export const NavigateAppInputZodSchema = z.discriminatedUnion('type', [
export const NavigateAppActionZodSchema = z.discriminatedUnion('type', [
z.object({
type: z
.literal('navigateToView')
@@ -59,4 +59,11 @@ export const NavigateAppInputZodSchema = z.discriminatedUnion('type', [
}),
]);
export const NavigateAppInputZodSchema = z.object({
navigation: NavigateAppActionZodSchema.describe(
'The navigation action to perform.',
),
});
export type NavigateAppAction = z.infer<typeof NavigateAppActionZodSchema>;
export type NavigateAppInput = z.infer<typeof NavigateAppInputZodSchema>;
@@ -54,7 +54,7 @@ export class NavigateAppTool implements Tool {
};
}
const input: NavigateAppInput = parseResult.data;
const input: NavigateAppInput['navigation'] = parseResult.data.navigation;
switch (input.type) {
case 'navigateToView':