/** * Elicitation Store Interface * * Abstraction for storing and routing elicitation state across distributed systems. * Supports storage backends with pub/sub for multi-node deployments and in-memory fallback for * single-node/development environments. * * Key design decisions: * - Pending records are keyed by sessionId (only one elicit per session allowed) * - Pub/sub channels are keyed by elicitId (for result routing) * - This separation allows form mode (lookup by session) and URL mode (direct elicitId) */ import { type ElicitMode, type ElicitResult, type FallbackExecutionResult, type FallbackResultCallback, type PendingElicitFallback, type ResolvedElicitResult } from '../elicitation.types'; /** * Record stored for each pending elicitation. * Keyed by sessionId since only one pending elicit per session is allowed. */ export interface PendingElicitRecord { /** Unique identifier for this elicit request (used for pub/sub routing) */ elicitId: string; /** Session ID that initiated the elicitation (used as storage key) */ sessionId: string; /** Timestamp when the elicitation was created */ createdAt: number; /** Absolute timestamp when the elicitation expires */ expiresAt: number; /** Message displayed to the user */ message: string; /** Elicitation mode (form or url) */ mode: ElicitMode; /** JSON Schema for validating result content (optional for backward compat) */ requestedSchema?: Record; } /** * Callback type for elicitation result subscriptions. */ export type ElicitResultCallback = (result: ElicitResult) => void; /** * Unsubscribe function returned by subscribeResult. */ export type ElicitUnsubscribe = () => Promise; /** * Elicitation store interface. * * Provides storage and pub/sub for elicitation state. In distributed deployments, * this enables routing elicitation responses to the correct node via Redis pub/sub. * * Storage is keyed by sessionId (only one pending elicit per session). * Pub/sub is keyed by elicitId (for cross-node result routing). * * @example * ```typescript * // Store pending elicitation (keyed by sessionId) * await store.setPending({ * elicitId: 'elicit-123', * sessionId: 'session-abc', * createdAt: Date.now(), * expiresAt: Date.now() + 300000, * message: 'Confirm action?', * mode: 'form', * }); * * // Subscribe to results by elicitId (on the node that sent the elicit) * const unsubscribe = await store.subscribeResult('elicit-123', (result) => { * console.log('Got result:', result); * }); * * // On any node: lookup by sessionId, then publish result by elicitId * const pending = await store.getPending('session-abc'); * if (pending) { * await store.publishResult(pending.elicitId, pending.sessionId, { status: 'accept', content: { confirmed: true } }); * } * ``` */ export interface ElicitationStore { /** * Store a pending elicitation with TTL. * The record is keyed by sessionId and will auto-expire based on `expiresAt`. * * @param record - The pending elicitation record to store */ setPending(record: PendingElicitRecord): Promise; /** * Get a pending elicitation by session ID. * Since only one elicit per session is allowed, sessionId is the lookup key. * * @param sessionId - The session ID to look up * @returns The pending record, or null if not found or expired */ getPending(sessionId: string): Promise; /** * Delete a pending elicitation by session ID. * Called after the elicitation is resolved or times out. * * @param sessionId - The session ID to delete */ deletePending(sessionId: string): Promise; /** * Subscribe to elicitation results for a specific elicit ID. * In distributed mode, this creates a Redis pub/sub subscription. * * @param elicitId - The elicitation ID to subscribe to * @param callback - Called when a result is published for this elicit ID * @param sessionId - Optional session ID for encrypted stores (required for decryption) * @returns Unsubscribe function to clean up the subscription */ subscribeResult(elicitId: string, callback: ElicitResultCallback, sessionId?: string): Promise; /** * Publish an elicitation result. * In distributed mode, this publishes via Redis pub/sub to reach the * correct node regardless of which node received the client response. * Also cleans up the pending record. * * @param elicitId - The elicitation ID * @param sessionId - The session ID (for cleanup) * @param result - The elicitation result to publish */ publishResult(elicitId: string, sessionId: string, result: ElicitResult): Promise; /** * Clean up store resources. * Called during server shutdown. */ destroy?(): Promise; /** * Store a pending elicitation fallback context. * Keyed by elicitId since we need to look up by elicitId when * sendElicitationResult is called. * * @param record - The pending fallback record to store */ setPendingFallback(record: PendingElicitFallback): Promise; /** * Get a pending elicitation fallback by elicit ID. * * @param elicitId - The elicitation ID to look up * @param sessionId - Optional session ID for encrypted stores (required for decryption) * @returns The pending fallback record, or null if not found or expired */ getPendingFallback(elicitId: string, sessionId?: string): Promise; /** * Delete a pending elicitation fallback by elicit ID. * * @param elicitId - The elicitation ID to delete */ deletePendingFallback(elicitId: string): Promise; /** * Store a resolved elicit result for re-invocation. * Used to pass the result to the tool when it's re-invoked. * * @param elicitId - The elicitation ID * @param result - The elicitation result from the user * @param sessionId - Optional session ID for encrypted stores (required for encryption) */ setResolvedResult(elicitId: string, result: ElicitResult, sessionId?: string): Promise; /** * Get a resolved elicit result by elicit ID. * * @param elicitId - The elicitation ID to look up * @param sessionId - Optional session ID for encrypted stores (required for decryption) * @returns The resolved result, or null if not found */ getResolvedResult(elicitId: string, sessionId?: string): Promise; /** * Delete a resolved elicit result by elicit ID. * * @param elicitId - The elicitation ID to delete */ deleteResolvedResult(elicitId: string): Promise; /** * Subscribe to fallback execution results for a specific elicit ID. * In distributed mode, this creates a Redis pub/sub subscription * for the channel `fallback-result:{elicitId}`. * * @param elicitId - The elicitation ID to subscribe to * @param callback - Called when a fallback result is published for this elicit ID * @param sessionId - Optional session ID for encrypted stores (required for decryption) * @returns Unsubscribe function to clean up the subscription */ subscribeFallbackResult(elicitId: string, callback: FallbackResultCallback, sessionId?: string): Promise; /** * Publish a fallback execution result. * In distributed mode, this publishes via Redis pub/sub to reach the * waiting node that originated the elicitation request. * * @param elicitId - The elicitation ID * @param sessionId - The session ID (for encryption) * @param result - The fallback execution result to publish */ publishFallbackResult(elicitId: string, sessionId: string, result: FallbackExecutionResult): Promise; } //# sourceMappingURL=elicitation.store.d.ts.map