/** * FlairStore — LangGraph BaseStore implementation backed by Flair. * * FlairStore stores JSON items in Flair under a configured agent identity. * Requests use Ed25519 when a key resolves, or configured administrator * Basic authentication when no key resolves. Other Flair clients can * retrieve the items when authorized; new items default to private and are * excluded from federation. * * FlairStore defines get, put, delete, search, batch, and listNamespaces. The first four dispatch directly; batch dispatches concurrently through Promise.all; listNamespaces builds and submits a batch operation. * * # Mapping * * LangGraph Flair * --------- ----- * namespace: string[] tags: ["lg-ns:"] (one tag) * key: string id suffix (full id: "lg:::") * value: object content: JSON.stringify(value) * search.query SemanticSearch q * search.filter (eq/gt/lt) applied client-side after retrieval * put(value=null) DELETE * * A namespace is stored in ONE form. Each label is escaped, then the labels * are joined by `/`, and that string is the whole `lg-ns:` tag and the middle * of the id. LangGraph forbids `.` in namespace labels, so `.` is the escape * character: `/` is written `.2F` and `:` is written `.3A`, and every other * character is unchanged. Earlier items under valid labels without `/` or `:` * retain their IDs and tags and need no migration. A label that is empty, or * that contains `.`, is invalid and is refused before any id or tag is built. * Both search paths filter on the client: the queryless path lists the agent's full item set and matches the * requested namespace PREFIX against the stored tag, and the semantic path * post-filters the namespace parsed from each id. * * # Limitations (v1) * * - FlairStore exposes no IndexConfig option and ignores the per-item index * argument. It sends each value as JSON content to Flair, which attempts * server-side embedding using its configured backend. To store fields * separately, extract them into separate items before calling put. * - `search.filter` operators ($eq/$ne/$gt/$gte/$lt/$lte) are applied * client-side after retrieving candidates. Filtering follows one backend * request, so large candidate sets can increase transfer and local processing. * - Namespace enumeration derives namespaces from the agent's newest 1,000 * memories, applies match conditions and maxDepth, and paginates the * distinct results. Namespaces without stored items cannot be enumerated, * and namespaces represented only outside that scan can be missed. * * # Auth * * FlairStore composes FlairClient, which signs requests with Ed25519 when a * key resolves and otherwise uses configured administrator Basic credentials * from adminUser/adminPassword or FLAIR_ADMIN_USER/FLAIR_ADMIN_PASSWORD. */ import type { Operation, OperationResults } from "@langchain/langgraph-checkpoint"; interface Item { value: Record; key: string; namespace: string[]; createdAt: Date; updatedAt: Date; } interface SearchItem extends Item { score?: number; } interface GetOperation { namespace: string[]; key: string; } interface SearchOperation { namespacePrefix: string[]; filter?: Record; limit?: number; offset?: number; query?: string; } interface PutOperation { namespace: string[]; key: string; value: Record | null; index?: false | string[]; } interface ListNamespacesOperation { matchConditions?: any[]; maxDepth?: number; limit: number; offset: number; } /** * Escape one namespace label for storage: `/` → `.2F`, `:` → `.3A`; every * other character is unchanged. A valid label contains no `.`, so the result * is unambiguous and decodeLabel reverses it exactly. */ export declare function encodeLabel(label: string): string; /** Decodes the output of encodeLabel for valid namespace labels. */ export declare function decodeLabel(encoded: string): string; /** * flair#1939 — does a stored full-namespace tag match this namespace PREFIX? * The prefix labels are encoded (the same one stored form), then * `lg-ns:` matches when the encoded prefix equals the tag's encoded * namespace or is a COMPONENT prefix of it (`lg-ns:a/b` matches a prefix of * `("a","b")` and `("a","b","c")`, but NOT `("a","bc")`, and a single label * `"a/b"` encodes to `a.2Fb` and does not match `("a",)`). An EMPTY prefix * matches every `lg-ns:` item, and a prefix with an invalid label matches * nothing. Earlier items under valid labels without `/` or `:` retain their * IDs and tags and need no migration. */ export declare function namespaceTagMatches(tags: unknown, prefix: string[]): boolean; /** * flair#1939 — does a decoded namespace satisfy one listNamespaces match * condition? Mirrors LangGraph's own store: `matchType` is `prefix` or * `suffix`, and `path` is the label pattern (a `*` element matches any label). */ export declare function namespaceMatchesCondition(condition: any, namespace: string[]): boolean; /** * Apply a single LangGraph filter operator. Mirrors BaseStore's documented * surface: $eq (default), $ne, $gt, $gte, $lt, $lte. Bare values are $eq. * * Exported for unit testing (Kern review on #370 — non-trivial logic with * 7 branches must have coverage). */ export declare function matchesFilter(value: any, condition: any): boolean; /** Apply all field filters in a search request. Logical AND across fields. */ export declare function matchesAllFilters(value: Record, filter: Record | undefined): boolean; /** * FlairStoreConfig requires agentId and exposes url, keyPath, a PEM-string * privateKey, adminUser, adminPassword, and timeoutMs; these values are * forwarded to the composed FlairClient. */ export interface FlairStoreConfig { /** Required. The Flair agent identity to scope all memories under. */ agentId: string; /** Flair URL. Defaults to FLAIR_URL env or http://localhost:19926. */ url?: string; /** Path to Ed25519 private key file. Auto-resolved from agent id if omitted. */ keyPath?: string; /** Or pass the key directly as PEM string. */ privateKey?: string; /** Basic-auth fallback for standalone deployments without Ed25519. */ adminUser?: string; adminPassword?: string; /** Request timeout in ms. Default 30s. */ timeoutMs?: number; } /** * FlairStore implements LangGraph's `BaseStore` interface and persists items * into Flair. It satisfies the interface structurally without importing the * abstract class directly (peer-dep pattern keeps the package install-light * if a host already has langgraph). * * Usage: * import { FlairStore } from "@tpsdev-ai/langgraph-flair"; * const store = new FlairStore({ agentId: "my-agent" }); * const graph = new StateGraph(...).compile({ store }); * * Or pass it to the agent directly: * const agent = createReactAgent({ llm, tools, store }); */ export declare class FlairStore { private client; private agentId; constructor(config: FlairStoreConfig); /** Dispatches the supplied operations concurrently and returns their results in input order. */ batch(operations: Op): Promise>; get(namespace: string[], key: string): Promise; put(namespace: string[], key: string, value: Record, index?: false | string[]): Promise; delete(namespace: string[], key: string): Promise; search(namespacePrefix: string[], options?: { filter?: Record; limit?: number; offset?: number; query?: string; }): Promise; /** * List namespaces seen in the agent's stored memories. `prefix` and `suffix` * become match conditions, exactly as LangGraph's `BaseStore.listNamespaces` * builds them; `limit` defaults to 100 and `offset` to 0. */ listNamespaces(options?: { prefix?: string[]; suffix?: string[]; maxDepth?: number; limit?: number; offset?: number; }): Promise; /** FlairStore has no background work to start, so this is a no-op. */ start(): void; /** FlairStore has no background work to stop, so this is a no-op. */ stop(): void; private dispatch; private doGet; private doPut; private doSearch; private doListNamespaces; } export declare function parseStoredId(id: string, agentId: string): { namespace: string[]; key: string; } | null; export declare function hasNamespacePrefix(namespace: string[], prefix: string[]): boolean; export type { Item, SearchItem, GetOperation, PutOperation, SearchOperation, ListNamespacesOperation }; //# sourceMappingURL=index.d.ts.map