diff --git a/src/agents/tools/cron-tool.test.ts b/src/agents/tools/cron-tool.test.ts index efab4535f..e53a87f1f 100644 --- a/src/agents/tools/cron-tool.test.ts +++ b/src/agents/tools/cron-tool.test.ts @@ -89,6 +89,33 @@ describe("cron tool", () => { }); }); + it("accepts cron.add job payloads as JSON strings", async () => { + const tool = createCronTool(); + const jobJson = JSON.stringify({ + name: "wake-up", + schedule: { atMs: 123 }, + payload: { kind: "systemEvent", text: "hello" }, + }); + await tool.execute("call-json", { + action: "add", + job: jobJson, + }); + + expect(callGatewayMock).toHaveBeenCalledTimes(1); + const call = callGatewayMock.mock.calls[0]?.[0] as { + method?: string; + params?: unknown; + }; + expect(call.method).toBe("cron.add"); + expect(call.params).toEqual({ + name: "wake-up", + schedule: { kind: "at", atMs: 123 }, + sessionTarget: "main", + wakeMode: "next-heartbeat", + payload: { kind: "systemEvent", text: "hello" }, + }); + }); + it("does not default agentId when job.agentId is null", async () => { const tool = createCronTool({ agentSessionKey: "main" }); await tool.execute("call-null", { diff --git a/src/agents/tools/cron-tool.ts b/src/agents/tools/cron-tool.ts index a1d218dd7..dfa974667 100644 --- a/src/agents/tools/cron-tool.ts +++ b/src/agents/tools/cron-tool.ts @@ -1,9 +1,9 @@ import { Type } from "@sinclair/typebox"; -import { normalizeCronJobCreate, normalizeCronJobPatch } from "../../cron/normalize.js"; import { loadConfig } from "../../config/config.js"; +import { normalizeCronJobCreate, normalizeCronJobPatch } from "../../cron/normalize.js"; import { truncateUtf16Safe } from "../../utils.js"; -import { optionalStringEnum, stringEnum } from "../schema/typebox.js"; import { resolveSessionAgentId } from "../agent-scope.js"; +import { optionalStringEnum, stringEnum } from "../schema/typebox.js"; import { type AnyAgentTool, jsonResult, readStringParam } from "./common.js"; import { callGatewayTool, type GatewayCallOptions } from "./gateway.js"; import { resolveInternalSessionKey, resolveMainSessionAlias } from "./sessions-helpers.js"; @@ -155,10 +155,21 @@ export function createCronTool(opts?: CronToolOptions): AnyAgentTool { }), ); case "add": { - if (!params.job || typeof params.job !== "object") { + if (params.job === null || params.job === undefined) { throw new Error("job required"); } - const job = normalizeCronJobCreate(params.job) ?? params.job; + let rawJob: unknown = params.job; + if (typeof rawJob === "string") { + try { + rawJob = JSON.parse(rawJob); + } catch { + throw new Error("job must be object"); + } + } + if (!rawJob || typeof rawJob !== "object" || Array.isArray(rawJob)) { + throw new Error("job must be object"); + } + const job = normalizeCronJobCreate(rawJob) ?? rawJob; if (job && typeof job === "object" && !("agentId" in job)) { const cfg = loadConfig(); const agentId = opts?.agentSessionKey