export type AgentReplacementTaskModeType = | "detail_replacement" | "sku_replacement"; export type AgentReplacementTaskStatus = | "pending" | "running" | "succeeded" | "failed"; export interface AgentReplacementTaskSnapshot { id: string; modeType: AgentReplacementTaskModeType; referenceId: string; messageId?: string | null; status: AgentReplacementTaskStatus; error: string | null; result: unknown; createdAt: string; startedAt: string | null; completedAt: string | null; queueStatus?: { queuePosition?: number | null; sameModelPendingCount?: number | null; sameModelRunningCount?: number | null; concurrencyLimit?: number | null; estimatedWaitMs?: number | null; } | null; } export type AgentReplacementCardStatus = | "waiting" | "queued" | "generating" | "completed" | "passthrough" | "fallback" | "failed" | "missing"; function toTimestamp(value: string | null | undefined) { const timestamp = Date.parse(String(value || "")); return Number.isFinite(timestamp) ? timestamp : 0; } export function buildLatestAgentReplacementTaskMap(params: { tasks: AgentReplacementTaskSnapshot[]; modeType: AgentReplacementTaskModeType; }) { const result = new Map(); const orderedTasks = [...params.tasks].sort( (left, right) => toTimestamp(right.createdAt) - toTimestamp(left.createdAt) ); for (const task of orderedTasks) { if ( task.modeType !== params.modeType || !task.referenceId || result.has(task.referenceId) ) { continue; } result.set(task.referenceId, task); } return result; } export function isAgentReplacementTaskInPendingRun(params: { task: AgentReplacementTaskSnapshot | null | undefined; pendingStartedAt?: number | null; requestMessageId?: string | null; clockSkewAllowanceMs?: number; }) { if (!params.task) { return false; } const taskMessageId = String(params.task.messageId || "").trim(); const requestMessageId = String(params.requestMessageId || "").trim(); if (taskMessageId && requestMessageId) { return taskMessageId === requestMessageId; } if (!Number.isFinite(Number(params.pendingStartedAt))) { return true; } const taskCreatedAt = toTimestamp(params.task.createdAt); const pendingStartedAt = Number(params.pendingStartedAt); const clockSkewAllowanceMs = Math.max(0, params.clockSkewAllowanceMs ?? 15_000); return taskCreatedAt >= pendingStartedAt - clockSkewAllowanceMs; } export function getAgentReplacementTaskResultImageUrl(result: unknown) { if (!result || typeof result !== "object" || Array.isArray(result)) { return null; } const images = (result as { images?: unknown }).images; if (!Array.isArray(images)) { return null; } for (const image of images) { if (!image || typeof image !== "object" || Array.isArray(image)) { continue; } const record = image as Record; const originalUrl = String(record.originalUrl || "").trim(); if (/^https?:\/\//i.test(originalUrl)) { return originalUrl; } const thumbnailUrl = String(record.thumbnailUrl || "").trim(); if (/^https?:\/\//i.test(thumbnailUrl)) { return thumbnailUrl; } } return null; } export function resolveAgentReplacementCardStatus(params: { resultUrl: string | null; persistedResultStatus?: "completed" | "passthrough" | "fallback" | null; taskStatus?: AgentReplacementTaskStatus | null; isRequestedInPendingRun: boolean; isGenerating: boolean; index: number; generatedCount: number; activeRunIndex?: number | null; }): AgentReplacementCardStatus { if (params.taskStatus === "pending") { return "queued"; } if (params.taskStatus === "running") { return "generating"; } if (params.resultUrl) { if ( params.persistedResultStatus === "passthrough" || params.persistedResultStatus === "fallback" ) { return params.persistedResultStatus; } if (params.taskStatus === "failed") { return "failed"; } return "completed"; } if (params.taskStatus === "failed") { return "failed"; } if (params.taskStatus === "succeeded") { return "missing"; } if (params.isRequestedInPendingRun) { return "queued"; } if ( typeof params.activeRunIndex === "number" && params.activeRunIndex >= 0 && params.generatedCount <= params.activeRunIndex ) { if (params.isGenerating) { return params.generatedCount === params.activeRunIndex ? "generating" : "queued"; } if (params.generatedCount > 0) { return "missing"; } } if (params.isGenerating) { return params.index === params.generatedCount ? "generating" : "queued"; } if (params.generatedCount > 0 && params.index >= params.generatedCount) { return "missing"; } return "waiting"; }