import type { WorkflowState, WorkflowStatus } from '../../types.ts'; import type { EngineInternals } from '../internals.ts'; import type { WorkflowStateCommitCallback } from './state-commit-callbacks.ts'; export type TerminationCallbacks = { dispatchEvent: (event: Event) => void; forwardEventToHandle: (workflowId: string, event: Event) => void; broadcast: (message: { type: string; workflowId: string; }) => void; swallowPromiseRejection: (promise: Promise | undefined) => Promise; handleCleanupError: (source: string, error: unknown, workflowId?: string) => void; handleScheduledWorkflowTerminal: (workflowId: string) => Promise; loadWorkflowState: (workflowId: string) => Promise; runSerializedWorkflowStateWrite: (workflowId: string, writeOperation: () => Promise) => Promise; /** SELF-transition (complete): fences on this engine's own claim. See ADR 0002. */ commitSelfWorkflowStateOperations: WorkflowStateCommitCallback; /** EXTERNAL terminal transition (suspend): rotates the claim epoch. See ADR 0002. */ commitExternalTerminalWorkflowStateOperations: WorkflowStateCommitCallback; cleanupReviews: (workflowId: string) => Promise; }; export declare const TERMINAL_WORKFLOW_STATUSES: ReadonlySet; /** * Non-terminal statuses a workflow can be *forcibly* terminated from — cancel/ * timeout (`terminateWorkflow`) or system fail (`failWorkflow`). Both read this * single constant so they cannot drift. `'suspended'` is included (a paused run * must still be reachable by cancel/fail, else the CAS no-ops and the run is * stranded); `completeWorkflow` deliberately excludes it (a suspended run's * generator is evicted, so it can never reach normal completion). */ export declare const FORCIBLY_TERMINABLE_STATUSES: readonly ["running", "pending", "suspended"]; /** * Remove any pending signal, update, and sleep waiters for a workflow. This * prevents memory leaks and ensures that cancelled/completed/failed workflows * cannot accept new signals, updates, or resolve orphaned sleep timers. */ export declare function cleanupWaiters(internals: EngineInternals, workflowId: string, callbacks: Pick): void; /** * Evict only the in-flight OPERATION waiters for a workflow that is being * suspended (signal/update/review waiters, review escalations, and sleep * resolvers), WITHOUT resolving them and WITHOUT touching the non-serialized * `services`, headers, type, or nesting-depth bookkeeping. * * Suspend parks the inline run (evicting its context/generator), so a signal or * update arriving afterwards must NOT wake the dormant operation loop and drive * the gone generator — it should buffer durably and be replayed when the * workflow resumes. Deleting (not resolving) each waiter leaves the loop dormant * and severs that wake path; the durable signal/sleep state in storage is * untouched, so resume replays it. This is the suspend-specific complement to * {@link cleanupWaiters}, which is for terminal transitions and additionally * drops `services` and resolves sleep resolvers — both wrong for a pause. */ export declare function evictSuspendedWorkflowWaiters(internals: EngineInternals, workflowId: string, callbacks: Pick): void; /** * Remove durable records keyed by `workflowId` that otherwise leak after a * workflow reaches a terminal state. * * - When `includeOutputArtifacts` is `false` (used by `completeWorkflow` * and `failWorkflow`), only internal bookkeeping is swept: pending * signals. Output artifacts - offloaded values, blob stream chunks, * shared state, and event history - are preserved so consumers can * still read them via `getStreamChunks()`, `getOffload()`, * `Engine.getEvents()`, etc. after `handle.result()` resolves. * - When `includeOutputArtifacts` is `true` (used by `terminateWorkflow`), * the workflow has been cancelled or timed out and no consumer is * waiting on output artifacts, so everything except `ev:` (preserved * for the events endpoint) is removed. * * Concurrency note: we assume all writers for a workflow's prefixed keys * originate from that workflow's own execution. By the time this runs, the * workflow is already terminal and cannot schedule new writes. The * persisted `terminal-cleanup` timer invokes this after terminalization, so * any write that races the scan must have come from a background task that * itself still holds a handle to the terminal workflow. Those are * caller-level bugs we don't try to paper over here. * * Scale note: deletes are flushed in batches of `CLEANUP_BATCH_SIZE` so * workflows with many blobs/signals do not allocate a single oversized * operation array. */ export declare function cleanupWorkflowStorage(internals: EngineInternals, workflowId: string, includeOutputArtifacts: boolean): Promise; /** * Shared synchronous cleanup invoked from every terminal-state transition * before result delivery. Drops only in-memory state so workflow resolution * is no longer blocked on storage cleanup. Durable scratch cleanup is * retried later through a persisted `terminal-cleanup` timer. * */ export declare function cleanupTerminalWorkflowMemory(internals: EngineInternals, workflowId: string, callbacks: Pick): void; export declare function cleanupTerminalWorkflowImmediately(internals: EngineInternals, workflowId: string, callbacks: TerminationCallbacks): void; export declare function cleanupTerminalWorkflowSynchronously(internals: EngineInternals, workflowId: string, includeOutputArtifacts: boolean, callbacks: TerminationCallbacks): Promise; export declare function cleanupTerminalWorkflowDurableState(internals: EngineInternals, workflowId: string, includeOutputArtifacts: boolean, callbacks: TerminationCallbacks): Promise; export declare function runDeferredTerminalCleanup(internals: EngineInternals, workflowId: string, timerId: string, callbacks: TerminationCallbacks): Promise; export declare function handleCleanupError(_internals: EngineInternals, source: string, error: unknown, workflowId: string | undefined, callbacks: Pick): void; export declare function finalizeScheduledWorkflowTerminal(_internals: EngineInternals, workflowId: string, callbacks: Pick): Promise;