import type { LTEscalationRecord } from '../../types'; import type { CreateEscalationInput, ClaimResult } from './types'; export declare function createEscalation(input: CreateEscalationInput): Promise; /** * Atomic claim. Implicit model — status stays 'pending'; "claimed" is * assigned_to + assigned_until > NOW(). `isExtension` is true when the same * user re-claims (extends expiry). Returns null when the row is not claimable. */ export declare function claimEscalation(id: string, userId: string, durationMinutes?: number): Promise; /** * Mark an escalation resolved. Signal delivery is owned by the resolution * orchestrator (api/escalations/resolve.ts); service-created rows have no * signal_key, so this never delivers a signal itself. Returns null when the * row is missing or already terminal. */ export declare function resolveEscalation(id: string, resolverPayload: Record): Promise; /** * Look up an efficient (atomic) escalation by its `signal_key` — the signal id * passed to `conditionLT(signalId, config)` / `condition(signalId, config)`. * Returns null when no row carries that key. */ export declare function getEscalationBySignalKey(signalKey: string): Promise; /** * Resolve an efficient (atomic) escalation by its `signal_key` and resume the * waiting workflow in place. Convenience for webhook callers that know the * deterministic signal id (e.g. `signal-scan-ar-${orderId}`) and want to skip * the id lookup. Returns null when the key is unknown or already terminal. * * Race-free: `signal_key → id` is an immutable mapping, and the state mutation * is delegated to `resolveEscalation`, whose `client.resolve` uses FOR UPDATE + * `WHERE status = 'pending'` so exactly one concurrent caller commits. No status * pre-check (that would be a TOCTOU window) — the atomic resolve is the arbiter. */ export declare function resolveEscalationBySignalKey(signalKey: string, resolverPayload: Record): Promise; /** * Mark an escalation cancelled — used when the tied workflow has terminated and * can never receive the resolution signal. The escalation is removed from the * active queue but preserved for audit. Returns null when the row is missing or * already terminal. */ export declare function cancelEscalation(id: string): Promise; /** * Bulk update priority for a set of escalations. Only pending escalations are * updated. */ export declare function updateEscalationsPriority(ids: string[], priority: 1 | 2 | 3 | 4): Promise; /** * Get the distinct roles for a set of escalation IDs. * Used for permission validation before bulk operations. */ export declare function getEscalationRoles(ids: string[]): Promise; /** * Release a single escalation claim back to the available pool. * Only the assigned user (or superadmin via route) may release. */ export declare function releaseEscalation(id: string, userId: string): Promise; /** * Sweep expired claims back to the available pool, returning the count cleared. * Availability is already query-time in the implicit model, but long-tail's * public contract clears `assigned_to` and returns a count, so this runs as a * single direct UPDATE on the shared table (the SDK's releaseExpired is a no-op). */ export declare function releaseExpiredClaims(): Promise; /** * Reassign an escalation to a different role. * Clears the current assignment so it becomes available to the new role. */ export declare function escalateToRole(id: string, targetRole: string): Promise; export declare function getEscalation(id: string): Promise; export declare function getEscalationsByTaskId(taskId: string): Promise; export declare function getEscalationsByWorkflowId(workflowId: string): Promise; /** * Cancel all pending escalations tied to a workflow in one atomic UPDATE. * Called after workflow termination so escalations don't remain in the queue. * * HotMesh's interrupt() is supposed to do this atomically, but it filters by * `app_id = `. Long-tail's Durable client uses app_id='durable' * while escalations are stored with app_id='hmsh' — the WHERE never matches. * This single UPDATE bypasses the SDK entirely and closes that gap. */ export declare function cancelEscalationsByWorkflowId(workflowId: string): Promise; export declare function updateEscalationMetadata(id: string, patch: Record): Promise; export declare function enrichEscalationRouting(id: string, metadataPatch: Record, workflowFields: { workflowType?: string; workflowId?: string; taskQueue?: string; taskId?: string; }): Promise; export declare function getEscalationsByOriginId(originId: string): Promise; export declare function findByMetadata(key: string, value: string, status?: string, limit?: number, offset?: number): Promise<{ escalations: LTEscalationRecord[]; total: number; }>; /** * Atomic claim by metadata with inline RBAC and optional metadata merge. * The SDK enforces the role filter in SQL — callers without an allowed role * match zero rows. Returns `{ escalation, isExtension, candidatesExist }` or * null when nothing was claimed. * * @param allowedRoles — roles the caller can claim (null = no filter / global) */ export declare function claimByMetadata(key: string, value: string, userId: string, durationMinutes?: number, metadata?: Record, allowedRoles?: string[] | null): Promise<(ClaimResult & { candidatesExist: number; }) | null>; export interface ResolveByMetadataResult { /** * 'resolved' = done atomically in SQL (no signal backing). * 'signal_required' = signal backing present, caller must deliver the signal. * 'conflict' = signal_id row already claimed by a concurrent caller; skip re-signal. * 'not_found' = no pending escalation matched the metadata filter. */ outcome: 'resolved' | 'signal_required' | 'conflict' | 'not_found'; /** The resolved escalation (when outcome = 'resolved') */ escalation?: LTEscalationRecord; /** Legacy conditionLT signal info (when signalId is set, caller uses handle.signal) */ signalId?: string; /** Atomic conditionLT signal key (when signalKey is set, caller uses SDK resolve to atomically mark+signal) */ signalKey?: string; escalationId?: string; workflowId?: string; workflowType?: string; taskQueue?: string; } /** * Atomic resolve by metadata with signal guard, in a single CTE. * * Signal-backed rows (those carrying `metadata.signal_id`) are NOT resolved * here — long-tail signals the paused workflow and the workflow interceptor * resolves durably. If the workflow is gone the signal fails and the row stays * pending, which is the contract the route suite pins. This guard is long-tail * business logic over the shared table, so it runs as one atomic statement on * `hmsh_escalations` rather than through the generic SDK resolve. */ export declare function resolveByMetadataAtomic(key: string, value: string, userId: string, resolverPayload: Record, metadata?: Record, allowedRoles?: string[] | null): Promise;