UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

236 lines 11 kB
import { r as FileLockOptions } from "../file-lock-BkoQbRDt.js"; //#region src/plugin-sdk/persistent-dedupe.types.d.ts type PersistentDedupeBaseOptions = { /** Milliseconds a recorded key remains recent; `0` keeps keys until cache pruning. */ ttlMs: number; /** Maximum process-local cache entries used before consulting SQLite. */ memoryMaxSize: number; onDiskError?: (error: unknown) => void; }; /** Configuration for a SQLite plugin-state dedupe namespace cache. */ type PersistentDedupePluginStateOptions = PersistentDedupeBaseOptions & { /** Plugin id that owns the persisted dedupe namespace. */ pluginId: string; /** Prefix for persisted plugin-state namespaces; defaults to `persistent-dedupe`. */ namespacePrefix?: string; /** Maximum persisted entries retained per namespace. */ stateMaxEntries: number; /** Test/runtime env used to resolve the shared OpenClaw state database. */ env?: NodeJS.ProcessEnv; resolveFilePath?: undefined; fileMaxEntries?: undefined; lockOptions?: undefined; }; /** Legacy path-shaped configuration. Paths now name SQLite namespaces, not JSON files. */ type PersistentDedupeLegacyPathOptions = PersistentDedupeBaseOptions & { pluginId?: undefined; stateMaxEntries?: undefined; namespacePrefix?: undefined; /** Maximum persisted entries retained per legacy namespace. */ fileMaxEntries: number; /** Maps a namespace to the retired JSON path; used only to derive a stable SQLite namespace. */ resolveFilePath: (namespace: string) => string; /** Test/runtime env used to resolve the shared OpenClaw state database. */ env?: NodeJS.ProcessEnv; /** @deprecated File locks are ignored because persistence is SQLite-backed. */ lockOptions?: Partial<FileLockOptions>; }; /** Configuration for a persisted dedupe namespace cache. */ type PersistentDedupeOptions = PersistentDedupePluginStateOptions | PersistentDedupeLegacyPathOptions; /** Per-call options used when checking or recording a dedupe key. */ type PersistentDedupeCheckOptions = { /** Logical bucket for the key; omitted/blank values use `global`. */ namespace?: string; /** Test or replay timestamp override used for TTL checks and writes. */ now?: number; /** Per-call disk error hook, overriding the helper-level hook. */ onDiskError?: (error: unknown) => void; }; /** Disk-backed dedupe guard that records recently seen keys per namespace. */ type PersistentDedupe = { /** Returns true only when the key was not recently seen and was recorded for future checks. */ checkAndRecord: (key: string, options?: PersistentDedupeCheckOptions) => Promise<boolean>; /** Checks memory/disk recency without recording a new timestamp. */ hasRecent: (key: string, options?: PersistentDedupeCheckOptions) => Promise<boolean>; /** Removes a recorded key from process memory and persisted storage. */ forget: (key: string, options?: PersistentDedupeCheckOptions) => Promise<boolean>; /** Loads recent disk entries into memory for one namespace and returns the loaded count. */ warmup: (namespace?: string, onError?: (error: unknown) => void) => Promise<number>; /** Clears only process-local memory; persisted namespace files are left intact. */ clearMemory: () => void; /** Returns the current process-local cache size. */ memorySize: () => number; }; /** Claim attempt result for dedupe flows that need in-flight ownership. */ type ClaimableDedupeClaimResult = { kind: "claimed"; } | { kind: "duplicate"; } | { kind: "inflight"; pending: Promise<boolean>; }; /** Options for a claimable dedupe guard, either persistent or memory-only. */ type ClaimableDedupeOptions = PersistentDedupePluginStateOptions | PersistentDedupeLegacyPathOptions | { ttlMs: number; memoryMaxSize: number; pluginId?: undefined; stateMaxEntries?: undefined; namespacePrefix?: undefined; env?: undefined; resolveFilePath?: undefined; fileMaxEntries?: undefined; lockOptions?: undefined; onDiskError?: undefined; }; /** Dedupe guard that lets one caller own a key while others wait or detect duplicates. */ type ClaimableDedupe = { /** Starts ownership of a key, reports duplicates, or returns the active claim's pending result. */ claim: (key: string, options?: PersistentDedupeCheckOptions) => Promise<ClaimableDedupeClaimResult>; /** Records a claimed key as handled and resolves any waiters with the recorded result. */ commit: (key: string, options?: PersistentDedupeCheckOptions) => Promise<boolean>; /** Releases an active claim without recording it, rejecting waiters with the supplied error. */ release: (key: string, options?: { namespace?: string; error?: unknown; }) => void; /** Checks whether the key is recent without claiming or committing it. */ hasRecent: (key: string, options?: PersistentDedupeCheckOptions) => Promise<boolean>; /** Removes an active or committed key from memory and persisted storage when supported. */ forget?: (key: string, options?: PersistentDedupeCheckOptions) => Promise<boolean>; /** Warms persistent storage into memory when configured; memory-only guards return zero. */ warmup: (namespace?: string, onError?: (error: unknown) => void) => Promise<number>; /** Clears process-local caches and in-memory persistent state. */ clearMemory: () => void; /** Returns the current process-local cache size. */ memorySize: () => number; }; //#endregion //#region src/plugin-sdk/channel-replay-guard.d.ts type ReplayKeys = string | readonly (string | null | undefined)[] | null | undefined; type ChannelReplayCommitOptions = Omit<PersistentDedupeCheckOptions, "namespace">; type ChannelReplayClaimHandle = { readonly keys: readonly [string, ...string[]]; commit: (options?: ChannelReplayCommitOptions) => Promise<boolean>; release: (options?: { error?: unknown; }) => void; }; type ChannelReplayClaimResult = { kind: "claimed"; handle: ChannelReplayClaimHandle; } | { kind: "duplicate"; } | { kind: "inflight"; pending: Promise<boolean>; } | { kind: "invalid"; }; type ChannelReplayProcessResult<T> = { kind: "processed"; value: T; } | { kind: "duplicate"; } | { kind: "inflight"; pending: Promise<boolean>; }; type ChannelReplayErrorMode = "commit" | "release"; type ChannelReplayProcessOptions = { dedupe?: PersistentDedupeCheckOptions; onError?: ChannelReplayErrorMode | ((error: unknown) => ChannelReplayErrorMode); }; type ChannelReplayGuardParams<TEvent> = { dedupe: ClaimableDedupeOptions; buildReplayKey: (event: TEvent) => ReplayKeys; namespace?: (event: TEvent) => string | undefined; }; type ChannelReplayGuard<TEvent> = { claim: (event: TEvent, options?: PersistentDedupeCheckOptions) => Promise<ChannelReplayClaimResult>; shouldProcess: (event: TEvent, options?: PersistentDedupeCheckOptions) => Promise<boolean>; processGuarded: <T>(event: TEvent, process: () => Promise<T>, options?: ChannelReplayProcessOptions) => Promise<ChannelReplayProcessResult<T>>; hasRecent: (event: TEvent, options?: PersistentDedupeCheckOptions) => Promise<boolean>; forget: (event: TEvent, options?: PersistentDedupeCheckOptions) => Promise<boolean>; warmup: (namespace?: string, onError?: (error: unknown) => void) => Promise<number>; clearMemory: () => void; }; //#endregion //#region src/plugin-sdk/persistent-dedupe.d.ts export type PersistentDedupeEntry = { key: string; seenAt: number; }; export type PersistentDedupeLegacyJsonMigrationResult = { imported: number; skippedExpired: number; skippedInvalid: number; skippedExisting: number; removed: boolean; }; export type PersistentDedupeLegacyJsonMigrationOptions = PersistentDedupePluginStateOptions & { filePath: string; namespace: string; now?: number; removeFile?: boolean; }; export type PersistentDedupeLegacyJsonImportEntry = { key: string; value: PersistentDedupeEntry; ttlMs?: number; }; export declare function createPersistentDedupeImportEntry(params: { key: string; seenAt: number; ttlMs?: number; }): PersistentDedupeLegacyJsonImportEntry; export declare function resolvePersistentDedupePluginStateNamespace(options: { namespace: string; namespacePrefix?: string; }): string; export declare function listPersistentDedupeLegacyJsonFileEntries(options: { filePath: string; ttlMs: number; now?: number; }): Promise<PersistentDedupeLegacyJsonImportEntry[]>; export declare function shouldReplacePersistentDedupeEntry(params: { existingValue: unknown; incomingValue: unknown; }): boolean; /** Import one retired JSON dedupe cache file into plugin-state SQLite during doctor repair. */ export declare function migratePersistentDedupeLegacyJsonFile(options: PersistentDedupeLegacyJsonMigrationOptions): Promise<PersistentDedupeLegacyJsonMigrationResult>; /** Create a dedupe helper that combines in-memory fast checks with SQLite-backed state. */ export declare function createPersistentDedupe(options: PersistentDedupeOptions): PersistentDedupe; type ClaimLoopInflight = { kind: "inflight"; pending: Promise<boolean>; }; type ClaimLoopSettled = { kind: "claimed"; } | { kind: "duplicate"; } | { kind: "invalid"; }; /** Resolve a claim, waiting on an active owner and retrying only when its release allows it. */ export declare function runClaimableDedupeClaimLoop<TClaim extends ClaimLoopSettled>(claimNext: () => Promise<TClaim | ClaimLoopInflight>, retryAfterRejection: (error: unknown, rejectionCount: number) => boolean): Promise<TClaim | { kind: "duplicate"; }>; /** Create a claim/commit/release dedupe guard backed by memory and optional persistent storage. */ export declare function createClaimableDedupe(options: ClaimableDedupeOptions): ClaimableDedupe & Required<Pick<ClaimableDedupe, "forget">>; /** * Create an event-keyed replay guard whose claims own their settlement handles. * * Layering contract vs the durable ingress drain (`src/channels/message/ingress-queue.ts`): * the drain already rejects duplicate event ids durably — `complete()` tombstones the row * and enqueue is `ON CONFLICT DO NOTHING` for the tombstone retention window. A replay * guard on a drained channel is justified only when its identity or retention exceeds the * queue's: a *logical* message key that differs from the transport delivery id (Telegram: * `chat_id:message_id` vs `update_id` — debounce/media-group merges can re-surface a * constituent message under a fresh update_id only the guard sees), or a window longer * than the channel's tombstone retention. If the guard key would equal the drain event_id * and retention fits the tombstone window, delete the guard when adopting the drain. */ export declare function createChannelReplayGuard<TEvent>(params: ChannelReplayGuardParams<TEvent>): ChannelReplayGuard<TEvent>; //#endregion export type { ChannelReplayClaimHandle, ClaimableDedupe, ClaimableDedupeClaimResult, ClaimableDedupeOptions, PersistentDedupe, PersistentDedupeCheckOptions, PersistentDedupeLegacyPathOptions, PersistentDedupeOptions, PersistentDedupePluginStateOptions };