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 })), });