import { SmrtClassOptions } from '@happyvertical/smrt-core'; import { Lead } from '../models/Lead.js'; import { SalesActivity } from '../models/SalesActivity.js'; import { SalesRepresentative } from '../models/SalesRepresentative.js'; import { LeadStatus } from '../types.js'; /** Human follow-up kinds accepted by {@link LeadWorkflowService.recordActivity}. */ export declare const LEAD_HUMAN_ACTIVITY_KINDS: readonly ["note", "call", "email", "meeting"]; export type LeadHumanActivityKind = (typeof LEAD_HUMAN_ACTIVITY_KINDS)[number]; /** Maximum persisted text length for generic workflow summaries and reasons. */ export declare const MAX_LEAD_WORKFLOW_TEXT_LENGTH = 1000; /** Queue state for a lead's next actionable follow-up step. */ export type LeadWorkQueueState = 'terminal' | 'reopenable' | 'unassigned' | 'no_next_action' | 'overdue' | 'due_today' | 'upcoming'; /** Plain input consumed by {@link projectLeadWorkQueue}; safe for host view mappers. */ export interface LeadWorkQueueInput { status: LeadStatus; ownerRepId?: string | null; nextAction?: { dueAt?: Date | null; } | null; /** Inject a clock to make queue classification deterministic. */ now?: Date; /** * Optional IANA timezone used only for the calendar-day boundary. Omitting * it leaves the host/runtime timezone in control; CRM never imposes one. */ timeZone?: string; } /** Reusable, policy-free work-queue projection. */ export interface LeadWorkQueueProjection { state: LeadWorkQueueState; isActionable: boolean; isUnassigned: boolean; hasNoNextAction: boolean; isOverdue: boolean; isDueToday: boolean; isUpcoming: boolean; isTerminal: boolean; isReopenable: boolean; } /** * Classify a lead without assuming an SLA, automatic owner, or timezone. * Terminal qualified/merged rows are never queued; disqualified leads remain * explicitly reopenable rather than being conflated with immutable terminals. */ export declare function projectLeadWorkQueue(input: LeadWorkQueueInput): LeadWorkQueueProjection; /** Details returned to reusable UI after one tenant-safe work-state read. */ export interface LeadWorkState { lead: Lead; owner: SalesRepresentative | null; earliestOpenTask: SalesActivity | null; queue: LeadWorkQueueProjection; } export interface AssignLeadInput { leadId: string; ownerRepId: string; actorProfileId: string; now?: Date; } export interface AssignLeadResult { lead: Lead; /** `false` when the active representative already owned the Lead. */ changed: boolean; } export interface StartWorkingInput { leadId: string; actorProfileId: string; now?: Date; } export interface DisqualifyLeadInput { leadId: string; actorProfileId: string; reason: string; now?: Date; } export interface RecordLeadActivityInput { leadId: string; actorProfileId: string; activityKind: LeadHumanActivityKind; summary: string; metadata?: Record; now?: Date; } export interface ScheduleLeadNextActionInput { leadId: string; actorProfileId: string; summary: string; dueAt: Date; metadata?: Record; now?: Date; } export interface CompleteLeadNextActionInput { leadId: string; taskId: string; actorProfileId: string; now?: Date; } export interface CompleteLeadNextActionResult { task: SalesActivity; /** `false` for a compatible exact replay after the task was completed. */ completed: boolean; } export interface GetLeadWorkStateInput { leadId: string; now?: Date; timeZone?: string; } /** Stable machine-readable refusal reasons for workflow callers. */ export type LeadWorkflowValidationReason = 'tenant_context_required' | 'transaction_unavailable' | 'lead_unavailable' | 'representative_unavailable' | 'representative_inactive' | 'lead_not_actionable' | 'invalid_transition' | 'reason_required' | 'reason_too_long' | 'summary_required' | 'summary_too_long' | 'invalid_activity_kind' | 'invalid_metadata' | 'invalid_due_at' | 'task_unavailable' | 'task_not_open' | 'completion_replay_conflict'; /** Workflow validation error that never reveals cross-tenant row existence. */ export declare class LeadWorkflowValidationError extends Error { readonly reason: LeadWorkflowValidationReason; readonly code: "LEAD_WORKFLOW_VALIDATION_ERROR"; constructor(reason: LeadWorkflowValidationReason, message: string); } /** * Reusable Lead follow-up service. Every mutation is one transaction that * locks the target lead (and task when completing), applies the guarded model * transition, and appends its immutable audit record before commit. */ export declare class LeadWorkflowService { private readonly deps; private constructor(); static create(options?: SmrtClassOptions): Promise; /** Assign or reassign an active representative, with one audit row per change. */ assignLead(input: AssignLeadInput): Promise; /** Start a new Lead or reopen a disqualified Lead for active follow-up. */ startWorking(input: StartWorkingInput): Promise; /** Disqualify a new or working Lead with a required bounded rationale. */ disqualifyLead(input: DisqualifyLeadInput): Promise; /** Append one immutable human note, call, email, or meeting to an active Lead. */ recordActivity(input: RecordLeadActivityInput): Promise; /** Schedule one open `task` activity for an active Lead. */ scheduleNextAction(input: ScheduleLeadNextActionInput): Promise; /** * Complete one open lead task exactly once. The task is locked with its Lead * so concurrent callers serialize; a compatible retry returns the completed * task without adding a second immutable completion event. */ completeNextAction(input: CompleteLeadNextActionInput): Promise; /** Return a deterministic chronological activity trail across merged Lead history. */ getLeadTimeline(leadId: string): Promise; /** Return the Lead, its visible owner, earliest open task, and queue projection. */ getLeadWorkState(input: GetLeadWorkStateInput): Promise; private runMutation; /** PostgreSQL gets a row lock; other adapters are serialized by runMutation. */ private lockLead; private lockLeadTask; private readLead; private assertActiveFollowUpLead; private appendAudit; private findTaskCompletionAudit; private requireActiveTenant; private canonicalMetadata; private requireBoundedText; private requireIdentifier; private requireValidDate; private requireLeadId; private requireActivityId; private now; private supportsRowLocks; private refusal; } export default LeadWorkflowService; //# sourceMappingURL=LeadWorkflowService.d.ts.map