/** * Idempotency, store and key generation. * * Provides an in-process idempotency store for tool-call deduplication across * replay, reconnect, and restart scenarios. Keys are deterministic (derived from * session + turn + call identifiers) so the same logical call always maps to the * same key regardless of when it is submitted. * * Lifecycle: * 1. Before executing a tool, call `checkAndRecord(key)`. * - `'new'` → proceed with execution. * - `'in-flight'` → a prior submission is still running; reject or wait. * - `'duplicate'` → a prior submission completed; return cached result. * 2. On success, call `markComplete(key, result)` to cache the result. * 3. On failure, call `markFailed(key)` to allow a retry on the next attempt. * * TTL-based eviction runs automatically whenever the store exceeds `maxRecords`. * In-flight records are never evicted. */ import type { IdempotencyKeyContext, IdempotencyRecord, IdempotencyStoreConfig } from './types.js'; export type { IdempotencyKeyContext, IdempotencyRecord, IdempotencyStoreConfig } from './types.js'; export type { IdempotencyStatus } from './types.js'; /** * IdempotencyStore, bounded, TTL-evicted in-process store. * * Thread safety: Node.js is single-threaded; no locking is required. * Suitable for in-process use within a single runtime session. * * @example * ```ts * const store = new IdempotencyStore({ ttlMs: 5 * 60_000 }); * const key = store.generateKey({ sessionId, turnId, callId }); * const check = store.checkAndRecord(key); * if (check.status === 'new') { * const result = await doWork(); * store.markComplete(key, result); * return result; * } else if (check.status === 'duplicate') { * return check.record.result; // cached * } else { * throw new Error('Tool call already in-flight'); * } * ``` */ export declare class IdempotencyStore { private readonly store; private readonly ttlMs; private readonly maxRecords; constructor(config?: IdempotencyStoreConfig); /** * Generate a deterministic idempotency key from a call context. * * Uses a SHA-256 hash of `sessionId:turnId:callId` to produce a compact, * collision-resistant key that is stable across restarts (given the same inputs). * * @param context - Session, turn, and call identifiers. * @returns Hex-encoded SHA-256 digest (64 characters). */ generateKey(context: IdempotencyKeyContext): string; /** * Check whether a key has been seen before and record it as `in-flight` if new. * * Returns a discriminated union: * - `{ status: 'new' }` , key is unseen; record created and marked in-flight. * - `{ status: 'in-flight' }` , a prior submission is still running. * - `{ status: 'duplicate', record }`, prior submission completed; record holds cached result. * * @param key - Idempotency key from `generateKey`. */ checkAndRecord(key: string): { readonly status: 'new'; } | { readonly status: 'in-flight'; readonly record: IdempotencyRecord; } | { readonly status: 'duplicate'; readonly record: IdempotencyRecord; }; /** * Mark a previously recorded key as `completed` and cache the result. * * Throws if the key is not in the store. In-flight records are not eligible * for eviction, so a missing key indicates an invalid finalization path. * * @param key - Idempotency key. * @param result - Optional result to cache for duplicate callers. */ markComplete(key: string, result?: unknown): void; /** * Mark a previously recorded key as `failed`, allowing a subsequent retry. * * The record transitions from `in-flight` → `failed`. On the next call to * `checkAndRecord` with the same key the failed record is deleted and the * caller receives `'new'`, allowing a fresh retry. * * Throws if the key is not in the store. In-flight records are not eligible * for eviction, so a missing key indicates an invalid finalization path. * * @param key - Idempotency key. */ markFailed(key: string): void; /** * Look up a record by key without changing its state. * * @param key - Idempotency key. * @returns The record, or `undefined` if not found or already evicted. */ getRecord(key: string): IdempotencyRecord | undefined; /** * Returns the current number of records in the store (including in-flight). */ get size(): number; /** * Evict completed and failed records whose age exceeds the configured TTL. * * Called automatically by `checkAndRecord` when the store is near capacity. * May also be called explicitly (e.g. on a periodic timer). * * In-flight records are never evicted. */ sweep(): void; /** Trigger a sweep when the store reaches 80% of the configured maximum. */ private _maybeSweep; } //# sourceMappingURL=index.d.ts.map