From 21cfc21e4f2035fb4521e10419cf7bde84c0a521 Mon Sep 17 00:00:00 2001 From: Ayaan Zaidi Date: Sun, 19 Apr 2026 21:01:00 +0530 Subject: [PATCH] fix(cron): key delivery dedupe by execution --- src/cron/isolated-agent/delivery-dispatch.ts | 11 +++++++---- src/cron/isolated-agent/run.ts | 1 - 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/cron/isolated-agent/delivery-dispatch.ts b/src/cron/isolated-agent/delivery-dispatch.ts index 2a4290aca56..b33d91477fa 100644 --- a/src/cron/isolated-agent/delivery-dispatch.ts +++ b/src/cron/isolated-agent/delivery-dispatch.ts @@ -22,6 +22,7 @@ import { normalizeOptionalLowercaseString, normalizeOptionalString, } from "../../shared/string-coerce.js"; +import { createCronExecutionId } from "../run-id.js"; import { hasScheduledNextRunAtMs } from "../service/jobs.js"; import type { CronJob, CronRunTelemetry } from "../types.js"; import type { DeliveryTargetResolution } from "./delivery-target.js"; @@ -102,7 +103,6 @@ type DispatchCronDeliveryParams = { job: CronJob; agentId: string; agentSessionKey: string; - runSessionId: string; runStartedAt: number; runEndedAt: number; timeoutMs: number; @@ -299,16 +299,18 @@ function getCompletedDirectCronDelivery( } function buildDirectCronDeliveryIdempotencyKey(params: { - runSessionId: string; + jobId: string; + runStartedAt: number; delivery: SuccessfulDeliveryTarget; }): string { + const executionId = createCronExecutionId(params.jobId, params.runStartedAt); const threadId = params.delivery.threadId == null || params.delivery.threadId === "" ? "" : String(params.delivery.threadId); const accountId = params.delivery.accountId?.trim() ?? ""; const normalizedTo = normalizeDeliveryTarget(params.delivery.channel, params.delivery.to); - return `cron-direct-delivery:v1:${params.runSessionId}:${params.delivery.channel}:${accountId}:${normalizedTo}:${threadId}`; + return `cron-direct-delivery:v1:${executionId}:${params.delivery.channel}:${accountId}:${normalizedTo}:${threadId}`; } function shouldQueueCronAwareness(job: CronJob, deliveryBestEffort: boolean): boolean { @@ -498,7 +500,8 @@ export async function dispatchCronDelivery( } = await loadDeliveryOutboundRuntime(); const identity = resolveAgentOutboundIdentity(params.cfgWithAgentDefaults, params.agentId); const deliveryIdempotencyKey = buildDirectCronDeliveryIdempotencyKey({ - runSessionId: params.runSessionId, + jobId: params.job.id, + runStartedAt: params.runStartedAt, delivery, }); try { diff --git a/src/cron/isolated-agent/run.ts b/src/cron/isolated-agent/run.ts index 5f0c32b0072..fc1580b9da1 100644 --- a/src/cron/isolated-agent/run.ts +++ b/src/cron/isolated-agent/run.ts @@ -666,7 +666,6 @@ async function finalizeCronRun(params: { job: prepared.input.job, agentId: prepared.agentId, agentSessionKey: prepared.agentSessionKey, - runSessionId: prepared.runSessionId, runStartedAt: execution.runStartedAt, runEndedAt: execution.runEndedAt, timeoutMs: prepared.timeoutMs,