Skip to content

Commit 6709f4e

Browse files
fix(cron): respect isolated target and error on missing remove id (#86234)
1 parent 0580f57 commit 6709f4e

4 files changed

Lines changed: 63 additions & 3 deletions

File tree

src/agents/tools/cron-tool.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,25 @@ describe("cron tool", () => {
630630
expect(sessionKey).toBe("agent:main:telegram:group:-100123:topic:99");
631631
});
632632

633+
it("does not stamp caller sessionKey when add targets isolated session", async () => {
634+
callGatewayMock.mockResolvedValueOnce({ ok: true });
635+
636+
const tool = createTestCronTool({ agentSessionKey: "agent:main:webchat:dm:dashboard" });
637+
await tool.execute("call-isolated-no-stamp", {
638+
action: "add",
639+
job: {
640+
name: "isolated run",
641+
schedule: { at: new Date(123).toISOString() },
642+
sessionTarget: "isolated",
643+
payload: { kind: "agentTurn", message: "hello" },
644+
},
645+
});
646+
const call = readGatewayCall();
647+
const payload = call.params as { sessionKey?: string; sessionTarget?: string } | undefined;
648+
expect(payload?.sessionTarget).toBe("isolated");
649+
expect(payload).not.toHaveProperty("sessionKey");
650+
});
651+
633652
it("adds recent context for systemEvent reminders when contextMessages > 0", async () => {
634653
callGatewayMock
635654
.mockResolvedValueOnce({

src/agents/tools/cron-tool.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,10 @@ Use jobId canonical; id accepted compat. contextMessages (0-10) adds previous me
670670
(job as { agentId?: string }).agentId = agentId;
671671
}
672672
}
673-
if (!("sessionKey" in job) && resolvedSessionKey) {
673+
const sessionTarget = normalizeLowercaseStringOrEmpty(
674+
(job as { sessionTarget?: unknown }).sessionTarget,
675+
);
676+
if (!("sessionKey" in job) && resolvedSessionKey && sessionTarget !== "isolated") {
674677
(job as { sessionKey?: string }).sessionKey = resolvedSessionKey;
675678
}
676679
}

src/gateway/server-methods/cron.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,9 +477,15 @@ export const cronHandlers: GatewayRequestHandlers = {
477477
return;
478478
}
479479
const result = await context.cron.remove(jobId);
480-
if (result.removed) {
481-
context.logGateway.info("cron: job removed", { jobId });
480+
if (!result.removed) {
481+
respond(
482+
false,
483+
undefined,
484+
errorShape(ErrorCodes.INVALID_REQUEST, "invalid cron.remove params: id not found"),
485+
);
486+
return;
482487
}
488+
context.logGateway.info("cron: job removed", { jobId });
483489
respond(true, result, undefined);
484490
},
485491
"cron.run": async ({ params, respond, context }) => {

src/gateway/server-methods/cron.validation.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ function createCronContext(currentJob?: CronJob) {
7575
cron: {
7676
add: vi.fn(async () => ({ id: "cron-1" })),
7777
update: vi.fn(async () => ({ id: "cron-1" })),
78+
remove: vi.fn(async () => ({ ok: true, removed: true })),
7879
getDefaultAgentId: vi.fn(() => "main"),
7980
getJob: vi.fn(() => currentJob),
8081
wake: vi.fn(() => ({ ok: true }) as const),
@@ -129,6 +130,26 @@ async function invokeCronUpdate(params: Record<string, unknown>, currentJob: Cro
129130
return { context, respond };
130131
}
131132

133+
async function invokeCronRemove(
134+
params: Record<string, unknown>,
135+
options?: { removeResult?: { ok: boolean; removed: boolean } },
136+
) {
137+
const context = createCronContext();
138+
if (options?.removeResult) {
139+
context.cron.remove.mockResolvedValueOnce(options.removeResult);
140+
}
141+
const respond = vi.fn();
142+
await cronHandlers["cron.remove"]({
143+
req: {} as never,
144+
params: params as never,
145+
respond: respond as never,
146+
context: context as never,
147+
client: null,
148+
isWebchatConnect: () => false,
149+
});
150+
return { context, respond };
151+
}
152+
132153
function createCronJob(overrides: Partial<CronJob> = {}): CronJob {
133154
return {
134155
id: "cron-1",
@@ -246,6 +267,17 @@ describe("cron method validation", () => {
246267
expect(respond).toHaveBeenCalledWith(true, { id: "cron-1" }, undefined);
247268
});
248269

270+
it("returns invalid-request error when cron.remove target id is missing", async () => {
271+
const { respond } = await invokeCronRemove(
272+
{ id: "missing-id" },
273+
{ removeResult: { ok: true, removed: false } },
274+
);
275+
expectResponseError(respond, {
276+
code: "INVALID_REQUEST",
277+
messageIncludes: "invalid cron.remove params: id not found",
278+
});
279+
});
280+
249281
it("returns a single cron job for cron.get", async () => {
250282
const job = createCronJob({ id: "cron-42", name: "single job" });
251283

0 commit comments

Comments
 (0)