diff --git a/CHANGELOG.md b/CHANGELOG.md index b5dc3f738..2c3e24e66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,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-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..cce894d44 100644 --- a/src/agents/tools/sessions-send-tool.ts +++ b/src/agents/tools/sessions-send-tool.ts @@ -30,25 +30,15 @@ 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 }, +const SessionsSendToolSchema = Type.Object({ + sessionKey: Type.Optional(Type.String()), + label: Type.Optional( + Type.String({ minLength: 1, maxLength: SESSION_LABEL_MAX_LENGTH }), ), - 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 }, - ), -]); + agentId: Type.Optional(Type.String({ minLength: 1, maxLength: 64 })), + 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")]), ),