import type { ISchedule } from '../Common/Schedules/Schedules'; import type { ConversationEventType, ConversationStatus, ID } from './Conversation'; import type { OutboxEvent } from './Outbox'; export declare const CONVERSATION_SLA_POLICIES_COLLECTION = "conversation_sla_policies"; export declare const CONVERSATION_SLA_CYCLES_COLLECTION = "conversation_sla_cycles"; export declare const CONVERSATION_SLA_EVENTS_COLLECTION = "conversation_sla_events"; export type ConversationSlaMetric = 'first_response' | 'next_response' | 'assignment' | 'acceptance' | 'resolution'; /** * continuous counts natural elapsed time, including nights and holidays. * schedule counts only ranges allowed by the referenced customer ISchedule. */ export type ConversationSlaTimeCalculation = { mode: 'continuous'; scheduleId?: never; } | { mode: 'schedule'; scheduleId: Exclude; }; export interface ConversationSlaWarningThreshold { id: ID; percentage: number; } export interface ConversationSlaTarget { metric: ConversationSlaMetric; durationSeconds: number; warningThresholds: ConversationSlaWarningThreshold[]; } /** * Threshold received by the SLA policy write API. * * Existing thresholds keep their id. New thresholds may omit it so the * backend can assign the canonical identifier returned in * ConversationSlaPolicy. */ export interface ConversationSlaWarningThresholdInput { id?: ID; percentage: number; } /** * Target received by the SLA policy write API. * durationSeconds is stored as seconds regardless of the unit used by the UI. */ export interface ConversationSlaTargetInput { metric: ConversationSlaMetric; durationSeconds: number; warningThresholds: ConversationSlaWarningThresholdInput[]; } export type ConversationSlaResumeBehavior = 'continue' | 'restart' | 'recalculate'; export type ConversationSlaReopenBehavior = 'preserve' | 'restart' | 'recalculate' | 'do_not_start'; export interface ConversationSlaClockBehavior { /** * Conversation statuses that pause every running SLA cycle. * * Entering one of these statuses pauses the clock. Leaving all of them * resumes it according to onResume. An empty array means that conversation * status changes never pause the SLA clock. */ pauseOnStatuses: ConversationStatus[]; /** * Behavior applied when the conversation leaves the last status included * in pauseOnStatuses. * * continue: keeps the current cycle and its consumed/remaining time. * restart: resets the current cycle from the resume timestamp. * recalculate: keeps the applied policy snapshot and recalculates the * schedule-derived warnings/deadline. It does not select another policy * version. */ onResume: ConversationSlaResumeBehavior; /** * Behavior applied when a resolved/finalized conversation is reopened. * Reopening is independent from resuming a status-paused clock. * * preserve: preserves the previous cycle state. * restart: starts a new cycle from the reopen timestamp. * recalculate: recalculates the cycle using the applied policy snapshot. * do_not_start: does not start an SLA cycle because of the reopen action. */ onReopen: ConversationSlaReopenBehavior; } /** * Immutable, versioned policy referenced by ConversationQueue.sla. * Editing a published policy creates a new version. */ export interface ConversationSlaPolicy { id: ID; spaceId: ID; name: string; description?: string; enabled: boolean; version: number; timeCalculation: ConversationSlaTimeCalculation; targets: ConversationSlaTarget[]; clockBehavior: ConversationSlaClockBehavior; createdAt: Date; createdByUserId?: ID; updatedAt: Date; updatedByUserId?: ID; } /** * Editable definition sent by clients. * * It intentionally excludes policy id/version and audit fields controlled by * the backend. targets is full-replacement: omitted metrics are disabled in * the newly published policy version. */ export interface ConversationSlaPolicyDefinitionInput { name: string; description?: string; enabled: boolean; timeCalculation: ConversationSlaTimeCalculation; targets: ConversationSlaTargetInput[]; clockBehavior: ConversationSlaClockBehavior; } /** * Creates the first immutable version of an SLA policy. * * commandId provides idempotency for retries. The authenticated backend * context supplies createdByUserId and the audit timestamps. */ export interface CreateConversationSlaPolicyRequest { commandId: ID; spaceId: ID; definition: ConversationSlaPolicyDefinitionInput; } /** * Publishes a new immutable version of an existing SLA policy. * * expectedVersion implements optimistic concurrency. The backend rejects the * request when another version was published after the client loaded it. * version, updatedAt and updatedByUserId are always assigned by the backend. */ export interface PublishConversationSlaPolicyVersionRequest { commandId: ID; spaceId: ID; policyId: ID; expectedVersion: number; definition: ConversationSlaPolicyDefinitionInput; } export type ConversationSlaClockAction = 'start' | 'evaluate' | 'pause' | 'resume' | 'complete' | 'recalculate' | 'cancel'; export type ConversationSlaClockReason = 'conversation_created' | 'conversation_enqueued' | 'customer_message' | 'agent_response' | 'assignment_changed' | 'status_changed' | 'queue_transfer' | 'conversation_reopened' | 'deadline_due' | 'policy_override'; /** * Idempotent input for the conversation-sla-worker. * policyId/policyVersion identify the immutable policy snapshot to apply. */ export interface SlaClockCommand { commandId: ID; sourceEventId: ID; spaceId: ID; conversationId: ID; expectedConversationVersion: number; action: ConversationSlaClockAction; reason: ConversationSlaClockReason; metric?: ConversationSlaMetric; policyId: ID; policyVersion: number; queueId?: ID; occurredAt: Date; requestedAt: Date; } export type ConversationSlaCycleStatus = 'running' | 'paused' | 'met' | 'breached' | 'cancelled'; export interface ConversationSlaPausePeriod { startedAt: Date; endedAt?: Date; reason: ConversationSlaClockReason; sourceEventId: ID; } /** * Durable metric cycle. The target and time calculation are copied from the * selected policy version so later policy edits cannot alter active history. */ export interface ConversationSlaCycle { id: ID; spaceId: ID; conversationId: ID; metric: ConversationSlaMetric; sequence: number; status: ConversationSlaCycleStatus; policyId: ID; policyVersion: number; timeCalculation: ConversationSlaTimeCalculation; target: ConversationSlaTarget; startedAt: Date; deadlineAt?: Date; nextWarningAt?: Date; pausedAt?: Date; completedAt?: Date; breachedAt?: Date; cancelledAt?: Date; elapsedSeconds: number; pausedSeconds: number; pausePeriods: ConversationSlaPausePeriod[]; startedByEventId: ID; completedByEventId?: ID; version: number; updatedAt: Date; } export type ConversationSlaEventType = Extract; /** * Immutable SLA event. Corrections are represented by a later event. */ export interface ConversationSlaEvent { eventId: ID; sourceEventId: ID; type: ConversationSlaEventType; schemaVersion: 1; spaceId: ID; conversationId: ID; conversationVersion: number; cycleId: ID; metric: ConversationSlaMetric; policyId: ID; policyVersion: number; status: ConversationSlaCycleStatus; deadlineAt?: Date; warningThresholdId?: ID; reason: ConversationSlaClockReason; correlationId: ID; causationId: ID; occurredAt: Date; recordedAt: Date; } export type ConversationSlaOutboxEvent = OutboxEvent & { aggregateType: 'conversation'; eventType: `conversation.${ConversationSlaEventType}`; schemaVersion: 1; };