import { buildClaimUrl, createTaskClaimToken } from '../claim/claim-service.js' import { maskCode as maskCodeValue, maskPhone as maskPhoneValue, } from '../../utils/masking.js' import type { TaskRow } from '../../types/repository/rows.js' type TaskClaimLink = { token: string expiredAt: string | null claimUrl: string } type ErrorLike = { errorCode?: string code?: string } export function maskPhone(value: unknown): string { return maskPhoneValue(value) } export function maskCode(value: unknown): string { return maskCodeValue(value) } export function isRecoverableTaskSessionCloseError(error: ErrorLike | null | undefined): boolean { const errorCode = String(error?.errorCode || error?.code || '').trim() return errorCode === 'session_not_found' || errorCode === 'session_closed' } export function normalizeManualDispatchOutcome(value: unknown): 'delivered' | 'failed' { const normalized = String(value || '').trim().toLowerCase() if (normalized === 'failed') { return 'failed' } return 'delivered' } export function getTaskClaimExpiresAt(task: Partial | null | undefined): string | null { return task?.claim_expires_at || task?.primary_claim_expires_at || null } export async function ensureTaskClaimLink(task: TaskRow): Promise { const tokenStatus = String(task?.primary_claim_token_status || '').trim() const token = String(task?.primary_claim_token || task?.claim_token || '').trim() const expiredAt = getTaskClaimExpiresAt(task) if (tokenStatus === 'active' && token && !isClaimExpired(expiredAt)) { return { token, expiredAt, claimUrl: buildClaimUrl(token), } } const claimToken = await createTaskClaimToken(task.id) return { token: claimToken.token, expiredAt: claimToken.expired_at, claimUrl: claimToken.claimUrl, } } function isClaimExpired(expiredAt: string | null): boolean { if (!expiredAt) { return false } const timestamp = new Date(expiredAt).getTime() return Number.isFinite(timestamp) && timestamp <= Date.now() }