From 423eef462422e9ae8c6b98c05ea3daacbf794055 Mon Sep 17 00:00:00 2001 From: Keith the Silly Goose Date: Sat, 10 Jan 2026 07:42:32 +1300 Subject: [PATCH 1/2] fix: simplify tool schemas for Gemini compatibility Replaces Type.Integer with Type.Number and simplifies the sessions_send tool schema to avoid anyOf/oneOf unions that cause 400 errors with Google Cloud Code Assist API. --- src/agents/tools/sessions-history-tool.ts | 2 +- src/agents/tools/sessions-list-tool.ts | 6 +++--- src/agents/tools/sessions-send-tool.ts | 26 ++++++----------------- src/agents/tools/sessions-spawn-tool.ts | 4 ++-- 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/agents/tools/sessions-history-tool.ts b/src/agents/tools/sessions-history-tool.ts index 0cc378f7b..5288fc839 100644 --- a/src/agents/tools/sessions-history-tool.ts +++ b/src/agents/tools/sessions-history-tool.ts @@ -18,7 +18,7 @@ import { const SessionsHistoryToolSchema = Type.Object({ sessionKey: Type.String(), - limit: Type.Optional(Type.Integer({ minimum: 1 })), + limit: Type.Optional(Type.Number({ minimum: 1 })), includeTools: Type.Optional(Type.Boolean()), }); diff --git a/src/agents/tools/sessions-list-tool.ts b/src/agents/tools/sessions-list-tool.ts index 87108fad7..68e14506c 100644 --- a/src/agents/tools/sessions-list-tool.ts +++ b/src/agents/tools/sessions-list-tool.ts @@ -46,9 +46,9 @@ type SessionListRow = { const SessionsListToolSchema = Type.Object({ kinds: Type.Optional(Type.Array(Type.String())), - limit: Type.Optional(Type.Integer({ minimum: 1 })), - activeMinutes: Type.Optional(Type.Integer({ minimum: 1 })), - messageLimit: Type.Optional(Type.Integer({ minimum: 0 })), + limit: Type.Optional(Type.Number({ minimum: 1 })), + activeMinutes: Type.Optional(Type.Number({ minimum: 1 })), + messageLimit: Type.Optional(Type.Number({ minimum: 0 })), }); function resolveSandboxSessionToolsVisibility( diff --git a/src/agents/tools/sessions-send-tool.ts b/src/agents/tools/sessions-send-tool.ts index 1c6a06e93..7640d314b 100644 --- a/src/agents/tools/sessions-send-tool.ts +++ b/src/agents/tools/sessions-send-tool.ts @@ -30,25 +30,13 @@ import { resolvePingPongTurns, } from "./sessions-send-helpers.js"; -const SessionsSendToolSchema = Type.Union([ - Type.Object( - { - sessionKey: Type.String(), - message: Type.String(), - timeoutSeconds: Type.Optional(Type.Integer({ minimum: 0 })), - }, - { additionalProperties: false }, - ), - Type.Object( - { - label: Type.String({ minLength: 1, maxLength: SESSION_LABEL_MAX_LENGTH }), - agentId: Type.Optional(Type.String({ minLength: 1, maxLength: 64 })), - message: Type.String(), - timeoutSeconds: Type.Optional(Type.Integer({ minimum: 0 })), - }, - { additionalProperties: false }, - ), -]); +const SessionsSendToolSchema = Type.Object({ + sessionKey: Type.Optional(Type.String()), + label: Type.Optional(Type.String()), + agentId: Type.Optional(Type.String()), + message: Type.String(), + timeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })), +}); export function createSessionsSendTool(opts?: { agentSessionKey?: string; diff --git a/src/agents/tools/sessions-spawn-tool.ts b/src/agents/tools/sessions-spawn-tool.ts index e6260a38a..f4391e5c3 100644 --- a/src/agents/tools/sessions-spawn-tool.ts +++ b/src/agents/tools/sessions-spawn-tool.ts @@ -25,9 +25,9 @@ const SessionsSpawnToolSchema = Type.Object({ label: Type.Optional(Type.String()), agentId: Type.Optional(Type.String()), model: Type.Optional(Type.String()), - runTimeoutSeconds: Type.Optional(Type.Integer({ minimum: 0 })), + runTimeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })), // Back-compat alias. Prefer runTimeoutSeconds. - timeoutSeconds: Type.Optional(Type.Integer({ minimum: 0 })), + timeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })), cleanup: Type.Optional( Type.Union([Type.Literal("delete"), Type.Literal("keep")]), ), From 0edacd0469a9432fa12d9b6ca76505ad0a6d66b0 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 9 Jan 2026 20:17:46 +0100 Subject: [PATCH 2/2] fix: simplify session tool schemas for Gemini compatibility (#599) (thanks @mcinteerj) --- CHANGELOG.md | 1 + src/agents/clawdbot-tools.sessions.test.ts | 37 ++++++++++++++++++++++ src/agents/tools/sessions-send-tool.ts | 6 ++-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d526e1cf..1b1b4c7dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ - Discord: stop provider when gateway reconnects are exhausted and surface errors. (#514) — thanks @joshp123 - Agents: strip empty assistant text blocks from session history to avoid Claude API 400s. (#210) - Agents: scrub unsupported JSON Schema keywords from tool schemas for Cloud Code Assist API compatibility. (#567) — thanks @erikpr1994 +- Agents: simplify session tool schemas for Gemini compatibility. (#599) — thanks @mcinteerj - Auto-reply: preserve block reply ordering with timeout fallback for streaming. (#503) — thanks @joshp123 - Auto-reply: block reply ordering fix (duplicate PR superseded by #503). (#483) — thanks @AbhisekBasu1 - Auto-reply: avoid splitting outbound chunks inside parentheses. (#499) — thanks @philipp-spiess diff --git a/src/agents/clawdbot-tools.sessions.test.ts b/src/agents/clawdbot-tools.sessions.test.ts index c7df1cf79..1f34ff957 100644 --- a/src/agents/clawdbot-tools.sessions.test.ts +++ b/src/agents/clawdbot-tools.sessions.test.ts @@ -23,6 +23,43 @@ vi.mock("../config/config.js", async (importOriginal) => { import { createClawdbotTools } from "./clawdbot-tools.js"; describe("sessions tools", () => { + it("uses number (not integer) in tool schemas for Gemini compatibility", () => { + const tools = createClawdbotTools(); + const byName = (name: string) => { + const tool = tools.find((candidate) => candidate.name === name); + expect(tool).toBeDefined(); + if (!tool) throw new Error(`missing ${name} tool`); + return tool; + }; + + const schemaProp = (toolName: string, prop: string) => { + const tool = byName(toolName); + const schema = tool.parameters as { + anyOf?: unknown; + oneOf?: unknown; + properties?: Record; + }; + expect(schema.anyOf).toBeUndefined(); + expect(schema.oneOf).toBeUndefined(); + + const properties = schema.properties ?? {}; + const value = properties[prop] as { type?: unknown } | undefined; + expect(value).toBeDefined(); + if (!value) throw new Error(`missing ${toolName} schema prop: ${prop}`); + return value; + }; + + expect(schemaProp("sessions_history", "limit").type).toBe("number"); + expect(schemaProp("sessions_list", "limit").type).toBe("number"); + expect(schemaProp("sessions_list", "activeMinutes").type).toBe("number"); + expect(schemaProp("sessions_list", "messageLimit").type).toBe("number"); + expect(schemaProp("sessions_send", "timeoutSeconds").type).toBe("number"); + expect(schemaProp("sessions_spawn", "runTimeoutSeconds").type).toBe( + "number", + ); + expect(schemaProp("sessions_spawn", "timeoutSeconds").type).toBe("number"); + }); + it("sessions_list filters kinds and includes messages", async () => { callGatewayMock.mockReset(); callGatewayMock.mockImplementation(async (opts: unknown) => { diff --git a/src/agents/tools/sessions-send-tool.ts b/src/agents/tools/sessions-send-tool.ts index 7640d314b..cce894d44 100644 --- a/src/agents/tools/sessions-send-tool.ts +++ b/src/agents/tools/sessions-send-tool.ts @@ -32,8 +32,10 @@ import { const SessionsSendToolSchema = Type.Object({ sessionKey: Type.Optional(Type.String()), - label: Type.Optional(Type.String()), - agentId: Type.Optional(Type.String()), + label: Type.Optional( + Type.String({ minLength: 1, maxLength: SESSION_LABEL_MAX_LENGTH }), + ), + agentId: Type.Optional(Type.String({ minLength: 1, maxLength: 64 })), message: Type.String(), timeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })), });