From fe9a7c40820b1fddd896a6201827901cf40e3667 Mon Sep 17 00:00:00 2001 From: Sid Date: Sun, 1 Mar 2026 01:08:53 +0800 Subject: [PATCH] fix(cron): force main-target system events onto main session (#28898) Ignore persisted sessionKey overrides for sessionTarget=main jobs so cron system events consistently route to the agent main session after upgrades. Closes #28770 --- ...service.runs-one-shot-main-job-disables-it.test.ts | 8 ++++---- src/cron/service/timer.ts | 11 +++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/cron/service.runs-one-shot-main-job-disables-it.test.ts b/src/cron/service.runs-one-shot-main-job-disables-it.test.ts index 37079addef0..cc3d6cd46ad 100644 --- a/src/cron/service.runs-one-shot-main-job-disables-it.test.ts +++ b/src/cron/service.runs-one-shot-main-job-disables-it.test.ts @@ -509,7 +509,7 @@ describe("CronService", () => { await store.cleanup(); }); - it("passes agentId + sessionKey to runHeartbeatOnce for main-session wakeMode now jobs", async () => { + it("passes agentId and resolves main session for wakeMode now main jobs", async () => { const runHeartbeatOnce = vi.fn(async () => ({ status: "ran" as const, durationMs: 1 })); const { store, cron, enqueueSystemEvent, requestHeartbeatNow } = @@ -534,13 +534,13 @@ describe("CronService", () => { expect.objectContaining({ reason: `cron:${job.id}`, agentId: "ops", - sessionKey, + sessionKey: undefined, }), ); expect(requestHeartbeatNow).not.toHaveBeenCalled(); expect(enqueueSystemEvent).toHaveBeenCalledWith( "hello", - expect.objectContaining({ agentId: "ops", sessionKey }), + expect.objectContaining({ agentId: "ops", sessionKey: undefined }), ); cron.stop(); @@ -578,7 +578,7 @@ describe("CronService", () => { expect(requestHeartbeatNow).toHaveBeenCalledWith( expect.objectContaining({ reason: `cron:${job.id}`, - sessionKey, + sessionKey: undefined, }), ); expect(job.state.lastStatus).toBe("ok"); diff --git a/src/cron/service/timer.ts b/src/cron/service/timer.ts index 85c392584fd..42be9848294 100644 --- a/src/cron/service/timer.ts +++ b/src/cron/service/timer.ts @@ -640,9 +640,12 @@ export async function executeJobCore( : 'main job requires payload.kind="systemEvent"', }; } + // main-target cron jobs should always resolve via the agent's main session. + // Avoid forwarding persisted channel session keys from legacy records. + const targetMainSessionKey = undefined; state.deps.enqueueSystemEvent(text, { agentId: job.agentId, - sessionKey: job.sessionKey, + sessionKey: targetMainSessionKey, contextKey: `cron:${job.id}`, }); if (job.wakeMode === "now" && state.deps.runHeartbeatOnce) { @@ -659,7 +662,7 @@ export async function executeJobCore( heartbeatResult = await state.deps.runHeartbeatOnce({ reason, agentId: job.agentId, - sessionKey: job.sessionKey, + sessionKey: targetMainSessionKey, }); if ( heartbeatResult.status !== "skipped" || @@ -677,7 +680,7 @@ export async function executeJobCore( state.deps.requestHeartbeatNow({ reason, agentId: job.agentId, - sessionKey: job.sessionKey, + sessionKey: targetMainSessionKey, }); return { status: "ok", summary: text }; } @@ -698,7 +701,7 @@ export async function executeJobCore( state.deps.requestHeartbeatNow({ reason: `cron:${job.id}`, agentId: job.agentId, - sessionKey: job.sessionKey, + sessionKey: targetMainSessionKey, }); return { status: "ok", summary: text }; }