fix(tools): flatten gateway tool schema for Vertex AI compatibility
Claude API on Vertex AI (Cloud Code Assist / Antigravity) enforces strict
JSON Schema 2020-12 validation and rejects root-level anyOf without a
top-level type field.
TypeBox Type.Union compiles to { anyOf: [...] } which Anthropic's direct
API accepts but Vertex rejects with:
tools.11.custom.input_schema: JSON schema is invalid
This follows the same pattern used in browser-tool.ts which has the same
fix with an explanatory comment.
Flatten the schema to Type.Object with an action enum, matching how
browser tool handles this constraint.
This commit is contained in:
parent
52565864d1
commit
a51948a528
@ -5,44 +5,37 @@ import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
|||||||
import { type AnyAgentTool, jsonResult, readStringParam } from "./common.js";
|
import { type AnyAgentTool, jsonResult, readStringParam } from "./common.js";
|
||||||
import { callGatewayTool } from "./gateway.js";
|
import { callGatewayTool } from "./gateway.js";
|
||||||
|
|
||||||
const GatewayToolSchema = Type.Union([
|
const GATEWAY_ACTIONS = [
|
||||||
Type.Object({
|
"restart",
|
||||||
action: Type.Literal("restart"),
|
"config.get",
|
||||||
delayMs: Type.Optional(Type.Number()),
|
"config.schema",
|
||||||
reason: Type.Optional(Type.String()),
|
"config.apply",
|
||||||
|
"update.run",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
type GatewayAction = (typeof GATEWAY_ACTIONS)[number];
|
||||||
|
|
||||||
|
// NOTE: Using a flattened object schema instead of Type.Union([Type.Object(...), ...])
|
||||||
|
// because Claude API on Vertex AI rejects nested anyOf schemas as invalid JSON Schema.
|
||||||
|
// The discriminator (action) determines which properties are relevant; runtime validates.
|
||||||
|
const GatewayToolSchema = Type.Object({
|
||||||
|
action: Type.Unsafe<GatewayAction>({
|
||||||
|
type: "string",
|
||||||
|
enum: [...GATEWAY_ACTIONS],
|
||||||
}),
|
}),
|
||||||
Type.Object({
|
// restart
|
||||||
action: Type.Literal("config.get"),
|
delayMs: Type.Optional(Type.Number()),
|
||||||
gatewayUrl: Type.Optional(Type.String()),
|
reason: Type.Optional(Type.String()),
|
||||||
gatewayToken: Type.Optional(Type.String()),
|
// config.get, config.schema, config.apply, update.run
|
||||||
timeoutMs: Type.Optional(Type.Number()),
|
gatewayUrl: Type.Optional(Type.String()),
|
||||||
}),
|
gatewayToken: Type.Optional(Type.String()),
|
||||||
Type.Object({
|
timeoutMs: Type.Optional(Type.Number()),
|
||||||
action: Type.Literal("config.schema"),
|
// config.apply, update.run
|
||||||
gatewayUrl: Type.Optional(Type.String()),
|
raw: Type.Optional(Type.String()),
|
||||||
gatewayToken: Type.Optional(Type.String()),
|
sessionKey: Type.Optional(Type.String()),
|
||||||
timeoutMs: Type.Optional(Type.Number()),
|
note: Type.Optional(Type.String()),
|
||||||
}),
|
restartDelayMs: Type.Optional(Type.Number()),
|
||||||
Type.Object({
|
});
|
||||||
action: Type.Literal("config.apply"),
|
|
||||||
raw: Type.String(),
|
|
||||||
sessionKey: Type.Optional(Type.String()),
|
|
||||||
note: Type.Optional(Type.String()),
|
|
||||||
restartDelayMs: Type.Optional(Type.Number()),
|
|
||||||
gatewayUrl: Type.Optional(Type.String()),
|
|
||||||
gatewayToken: Type.Optional(Type.String()),
|
|
||||||
timeoutMs: Type.Optional(Type.Number()),
|
|
||||||
}),
|
|
||||||
Type.Object({
|
|
||||||
action: Type.Literal("update.run"),
|
|
||||||
sessionKey: Type.Optional(Type.String()),
|
|
||||||
note: Type.Optional(Type.String()),
|
|
||||||
restartDelayMs: Type.Optional(Type.Number()),
|
|
||||||
timeoutMs: Type.Optional(Type.Number()),
|
|
||||||
gatewayUrl: Type.Optional(Type.String()),
|
|
||||||
gatewayToken: Type.Optional(Type.String()),
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
export function createGatewayTool(opts?: {
|
export function createGatewayTool(opts?: {
|
||||||
agentSessionKey?: string;
|
agentSessionKey?: string;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user