/**
* 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